@trafica/editor 1.0.31 → 1.0.33
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 +15 -3
- package/dist/index.d.ts +15 -3
- package/dist/index.js +1038 -325
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1040 -327
- 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 { useSyncExternalStore, useRef, useCallback, useState, useEffect, useLayoutEffect, useMemo } from 'react';
|
|
2
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
3
3
|
import { createPortal } from 'react-dom';
|
|
4
4
|
|
|
5
5
|
// src/editor/core/DocumentModel.ts
|
|
@@ -659,9 +659,16 @@ function applyTransaction(state, tr) {
|
|
|
659
659
|
case "split_block":
|
|
660
660
|
doc = splitBlock(doc, step.path, step.offset);
|
|
661
661
|
break;
|
|
662
|
-
case "join_blocks":
|
|
662
|
+
case "join_blocks": {
|
|
663
|
+
const joinIdx = step.path[step.path.length - 1];
|
|
664
|
+
const prevPath = [...step.path.slice(0, -1), joinIdx - 1];
|
|
665
|
+
const prevNode = getNodeAtPath(doc, prevPath);
|
|
666
|
+
const prevTextLen = prevNode && "children" in prevNode ? collectText(prevNode).length : 0;
|
|
663
667
|
doc = joinBlocks(doc, step.path);
|
|
668
|
+
const joinPos = getPosFromCharOffset(doc, prevPath, prevTextLen);
|
|
669
|
+
selection = { anchor: joinPos, focus: joinPos, isCollapsed: true };
|
|
664
670
|
break;
|
|
671
|
+
}
|
|
665
672
|
case "set_node_type":
|
|
666
673
|
doc = setBlockType(doc, step.path, step.nodeType, step.attrs);
|
|
667
674
|
break;
|
|
@@ -7824,7 +7831,7 @@ var jsonSerializer = {
|
|
|
7824
7831
|
}
|
|
7825
7832
|
}
|
|
7826
7833
|
};
|
|
7827
|
-
function
|
|
7834
|
+
function Editor({
|
|
7828
7835
|
engine,
|
|
7829
7836
|
placeholder = "Start writing...",
|
|
7830
7837
|
className = "",
|
|
@@ -7832,14 +7839,11 @@ function EditorCore({
|
|
|
7832
7839
|
onHTMLChange,
|
|
7833
7840
|
onJSONChange,
|
|
7834
7841
|
onOpenLinkPopup,
|
|
7835
|
-
onUploadImage: _onUploadImage
|
|
7836
|
-
onFocus: onFocusProp,
|
|
7837
|
-
onBlur: onBlurProp,
|
|
7838
|
-
hideToolbar = false,
|
|
7839
|
-
toolbarConfig
|
|
7842
|
+
onUploadImage: _onUploadImage
|
|
7840
7843
|
}) {
|
|
7841
7844
|
const containerRef = useRef(null);
|
|
7842
7845
|
const isRenderingRef = useRef(false);
|
|
7846
|
+
const skipRestoreSelectionRef = useRef(false);
|
|
7843
7847
|
const scrollCaretIntoView = useCallback(() => {
|
|
7844
7848
|
var _a, _b;
|
|
7845
7849
|
const sel = window.getSelection();
|
|
@@ -7952,8 +7956,14 @@ function EditorCore({
|
|
|
7952
7956
|
useLayoutEffect(() => {
|
|
7953
7957
|
const container = containerRef.current;
|
|
7954
7958
|
if (!container || !state.selection) return;
|
|
7959
|
+
if (skipRestoreSelectionRef.current) {
|
|
7960
|
+
skipRestoreSelectionRef.current = false;
|
|
7961
|
+
return;
|
|
7962
|
+
}
|
|
7963
|
+
isRenderingRef.current = true;
|
|
7955
7964
|
restoreSelection(container, state.selection);
|
|
7956
7965
|
scrollCaretIntoView();
|
|
7966
|
+
isRenderingRef.current = false;
|
|
7957
7967
|
}, [state.selection]);
|
|
7958
7968
|
useEffect(() => {
|
|
7959
7969
|
const container = containerRef.current;
|
|
@@ -8018,6 +8028,7 @@ function EditorCore({
|
|
|
8018
8028
|
if (currentSelection && JSON.stringify(currentSelection.anchor) === JSON.stringify(captured.anchor) && JSON.stringify(currentSelection.focus) === JSON.stringify(captured.focus)) {
|
|
8019
8029
|
return;
|
|
8020
8030
|
}
|
|
8031
|
+
skipRestoreSelectionRef.current = true;
|
|
8021
8032
|
const tr = createTransaction();
|
|
8022
8033
|
tr.steps.push(tr_setSelection(captured));
|
|
8023
8034
|
engine.dispatch(tr);
|
|
@@ -8044,11 +8055,7 @@ function EditorCore({
|
|
|
8044
8055
|
);
|
|
8045
8056
|
engine.dispatch(tr);
|
|
8046
8057
|
}
|
|
8047
|
-
|
|
8048
|
-
}, [engine, onFocusProp]);
|
|
8049
|
-
const handleBlur = useCallback(() => {
|
|
8050
|
-
onBlurProp == null ? void 0 : onBlurProp();
|
|
8051
|
-
}, [onBlurProp]);
|
|
8058
|
+
}, [engine]);
|
|
8052
8059
|
const handleKeyDown = useCallback(
|
|
8053
8060
|
(e) => {
|
|
8054
8061
|
var _a, _b, _c;
|
|
@@ -8400,6 +8407,7 @@ function EditorCore({
|
|
|
8400
8407
|
if (!container) return;
|
|
8401
8408
|
const selection = captureSelection(container);
|
|
8402
8409
|
if (!selection) return;
|
|
8410
|
+
skipRestoreSelectionRef.current = true;
|
|
8403
8411
|
const tr = createTransaction();
|
|
8404
8412
|
tr.steps.push(
|
|
8405
8413
|
tr_setSelection(selection)
|
|
@@ -8482,7 +8490,7 @@ function EditorCore({
|
|
|
8482
8490
|
className: `editor-root relative flex flex-col ${className}`,
|
|
8483
8491
|
style: editorHeight ? { minHeight: editorHeight } : void 0,
|
|
8484
8492
|
children: [
|
|
8485
|
-
!readOnly &&
|
|
8493
|
+
!readOnly && /* @__PURE__ */ jsx(
|
|
8486
8494
|
Toolbar,
|
|
8487
8495
|
{
|
|
8488
8496
|
engine,
|
|
@@ -8493,8 +8501,7 @@ function EditorCore({
|
|
|
8493
8501
|
linkPopupOpen,
|
|
8494
8502
|
onLinkPopupClose: () => setLinkPopupOpen(false),
|
|
8495
8503
|
isSourceMode,
|
|
8496
|
-
onToggleSource: handleToggleSource
|
|
8497
|
-
toolbarConfig
|
|
8504
|
+
onToggleSource: handleToggleSource
|
|
8498
8505
|
}
|
|
8499
8506
|
),
|
|
8500
8507
|
/* @__PURE__ */ jsx("div", { className: "relative", children: isSourceMode ? /* @__PURE__ */ jsx(
|
|
@@ -8521,7 +8528,7 @@ function EditorCore({
|
|
|
8521
8528
|
isVisualEmpty && /* @__PURE__ */ jsx(
|
|
8522
8529
|
"div",
|
|
8523
8530
|
{
|
|
8524
|
-
className: "\n absolute\n top-6\n left-6\n pointer-events-none\n select-none\n text-base\n text-gray-400\n \n ",
|
|
8531
|
+
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 ",
|
|
8525
8532
|
"aria-hidden": "true",
|
|
8526
8533
|
children: placeholder
|
|
8527
8534
|
}
|
|
@@ -8539,7 +8546,6 @@ function EditorCore({
|
|
|
8539
8546
|
onMouseDown: handleMouseDown,
|
|
8540
8547
|
onContextMenu: handleContextMenu,
|
|
8541
8548
|
onFocus: handleFocus,
|
|
8542
|
-
onBlur: handleBlur,
|
|
8543
8549
|
onClick: handleClick,
|
|
8544
8550
|
onKeyDown: handleKeyDown,
|
|
8545
8551
|
className: [
|
|
@@ -8551,7 +8557,7 @@ function EditorCore({
|
|
|
8551
8557
|
"text-base",
|
|
8552
8558
|
"leading-relaxed",
|
|
8553
8559
|
"text-gray-900",
|
|
8554
|
-
"",
|
|
8560
|
+
"dark:text-gray-100",
|
|
8555
8561
|
readOnly ? "cursor-default" : "cursor-text"
|
|
8556
8562
|
].join(" ")
|
|
8557
8563
|
}
|
|
@@ -8561,7 +8567,7 @@ function EditorCore({
|
|
|
8561
8567
|
{
|
|
8562
8568
|
role: "tooltip",
|
|
8563
8569
|
style: { left: linkTooltip.x, top: linkTooltip.y },
|
|
8564
|
-
className: "absolute z-50 pointer-events-none flex items-center gap-1.5 bg-gray-900
|
|
8570
|
+
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",
|
|
8565
8571
|
children: [
|
|
8566
8572
|
/* @__PURE__ */ jsxs("svg", { width: "11", height: "11", viewBox: "0 0 16 16", fill: "none", className: "shrink-0 opacity-70", "aria-hidden": "true", children: [
|
|
8567
8573
|
/* @__PURE__ */ jsx("path", { d: "M6.5 3.5H4A2.5 2.5 0 0 0 4 8.5h2", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round" }),
|
|
@@ -8676,171 +8682,1023 @@ function prettyPrintHTML(html) {
|
|
|
8676
8682
|
}
|
|
8677
8683
|
return result.join("\n");
|
|
8678
8684
|
}
|
|
8679
|
-
|
|
8680
|
-
|
|
8681
|
-
|
|
8682
|
-
|
|
8683
|
-
|
|
8684
|
-
|
|
8685
|
-
|
|
8686
|
-
|
|
8687
|
-
|
|
8688
|
-
|
|
8689
|
-
|
|
8690
|
-
|
|
8691
|
-
|
|
8692
|
-
|
|
8693
|
-
|
|
8694
|
-
|
|
8695
|
-
|
|
8696
|
-
|
|
8697
|
-
|
|
8698
|
-
|
|
8699
|
-
|
|
8700
|
-
|
|
8701
|
-
|
|
8702
|
-
|
|
8703
|
-
|
|
8704
|
-
|
|
8705
|
-
|
|
8706
|
-
|
|
8707
|
-
|
|
8708
|
-
|
|
8709
|
-
|
|
8710
|
-
|
|
8711
|
-
|
|
8712
|
-
|
|
8713
|
-
|
|
8714
|
-
|
|
8715
|
-
|
|
8716
|
-
|
|
8717
|
-
|
|
8718
|
-
|
|
8719
|
-
|
|
8720
|
-
|
|
8721
|
-
|
|
8722
|
-
|
|
8723
|
-
return caretRect.top < containerRect.top + lineHeight;
|
|
8724
|
-
} else {
|
|
8725
|
-
return caretRect.bottom > containerRect.bottom - lineHeight;
|
|
8726
|
-
}
|
|
8727
|
-
}
|
|
8728
|
-
var TablePlugin = {
|
|
8729
|
-
name: "table",
|
|
8730
|
-
keyBindings: {
|
|
8731
|
-
Tab: (engine) => {
|
|
8732
|
-
var _a;
|
|
8733
|
-
const state = engine.getState();
|
|
8734
|
-
const sel = state.selection;
|
|
8735
|
-
if (!sel) return false;
|
|
8736
|
-
const cellPos = findCellPosition(state.doc, sel.anchor.path);
|
|
8737
|
-
if (!cellPos) return false;
|
|
8738
|
-
const { tablePath, row, col } = cellPos;
|
|
8739
|
-
const table = getNodeAtPath(state.doc, tablePath);
|
|
8740
|
-
const { rows, cols } = getTableDimensions(table);
|
|
8741
|
-
let nextRow = row;
|
|
8742
|
-
let nextCol = col + 1;
|
|
8743
|
-
while (nextRow < rows) {
|
|
8744
|
-
if (nextCol >= cols) {
|
|
8745
|
-
nextCol = 0;
|
|
8746
|
-
nextRow++;
|
|
8747
|
-
continue;
|
|
8748
|
-
}
|
|
8749
|
-
const c = getNodeAtPath(state.doc, [...tablePath, nextRow, nextCol]);
|
|
8750
|
-
if (!((_a = c == null ? void 0 : c.attrs) == null ? void 0 : _a.covered)) break;
|
|
8751
|
-
nextCol++;
|
|
8752
|
-
}
|
|
8753
|
-
if (nextRow >= rows) {
|
|
8754
|
-
const newTable = insertTableRowAfter(table, rows - 1);
|
|
8755
|
-
const newChildren = [...state.doc.children];
|
|
8756
|
-
newChildren[tablePath[0]] = newTable;
|
|
8757
|
-
const tempDoc = { type: "doc", children: newChildren };
|
|
8758
|
-
const tr2 = createTransaction();
|
|
8759
|
-
tr2.steps.push({ type: "delete_node", path: tablePath });
|
|
8760
|
-
tr2.steps.push({
|
|
8761
|
-
type: "insert_node",
|
|
8762
|
-
parentPath: tablePath.length > 1 ? tablePath.slice(0, -1) : [],
|
|
8763
|
-
index: tablePath[tablePath.length - 1],
|
|
8764
|
-
node: newTable
|
|
8765
|
-
});
|
|
8766
|
-
const pos2 = getCellFirstPosition(tempDoc, tablePath, rows, 0);
|
|
8767
|
-
if (pos2) tr2.steps.push(tr_setSelection(makeCollapsedSelection(pos2)));
|
|
8768
|
-
engine.dispatch(tr2);
|
|
8769
|
-
return true;
|
|
8770
|
-
}
|
|
8771
|
-
const pos = getCellFirstPosition(state.doc, tablePath, nextRow, nextCol);
|
|
8772
|
-
if (!pos) return false;
|
|
8773
|
-
const tr = createTransaction();
|
|
8774
|
-
tr.steps.push(tr_setSelection(makeCollapsedSelection(pos)));
|
|
8775
|
-
engine.dispatch(tr);
|
|
8776
|
-
return true;
|
|
8777
|
-
},
|
|
8778
|
-
"ArrowRight": (engine) => {
|
|
8779
|
-
var _a;
|
|
8780
|
-
const state = engine.getState();
|
|
8781
|
-
const sel = state.selection;
|
|
8782
|
-
if (!sel) return false;
|
|
8783
|
-
const cellPos = findCellPosition(state.doc, sel.anchor.path);
|
|
8784
|
-
if (!cellPos) return false;
|
|
8785
|
-
const { tablePath, row, col } = cellPos;
|
|
8786
|
-
const cellPath = [...tablePath, row, col];
|
|
8787
|
-
const cellEl = getCellDOMElement(JSON.stringify(cellPath));
|
|
8788
|
-
if (!cellEl || !isCaretAtContainerEnd(cellEl)) return false;
|
|
8789
|
-
const table = getNodeAtPath(state.doc, tablePath);
|
|
8790
|
-
const { rows, cols } = getTableDimensions(table);
|
|
8791
|
-
let nextRow = row;
|
|
8792
|
-
let nextCol = col + 1;
|
|
8793
|
-
while (nextRow < rows) {
|
|
8794
|
-
if (nextCol >= cols) {
|
|
8795
|
-
nextCol = 0;
|
|
8796
|
-
nextRow++;
|
|
8797
|
-
continue;
|
|
8798
|
-
}
|
|
8799
|
-
const c = getNodeAtPath(state.doc, [...tablePath, nextRow, nextCol]);
|
|
8800
|
-
if (!((_a = c == null ? void 0 : c.attrs) == null ? void 0 : _a.covered)) break;
|
|
8801
|
-
nextCol++;
|
|
8685
|
+
function EditorCore({
|
|
8686
|
+
engine,
|
|
8687
|
+
placeholder = "Start writing...",
|
|
8688
|
+
className = "",
|
|
8689
|
+
readOnly = false,
|
|
8690
|
+
onHTMLChange,
|
|
8691
|
+
onJSONChange,
|
|
8692
|
+
onOpenLinkPopup,
|
|
8693
|
+
onUploadImage: _onUploadImage,
|
|
8694
|
+
onFocus: onFocusProp,
|
|
8695
|
+
onBlur: onBlurProp,
|
|
8696
|
+
hideToolbar = false,
|
|
8697
|
+
toolbarConfig
|
|
8698
|
+
}) {
|
|
8699
|
+
const containerRef = useRef(null);
|
|
8700
|
+
const isRenderingRef = useRef(false);
|
|
8701
|
+
const scrollCaretIntoView = useCallback(() => {
|
|
8702
|
+
var _a, _b;
|
|
8703
|
+
const sel = window.getSelection();
|
|
8704
|
+
if (!sel || sel.rangeCount === 0) return;
|
|
8705
|
+
const range = sel.getRangeAt(0).cloneRange();
|
|
8706
|
+
range.collapse(true);
|
|
8707
|
+
const caretRect = range.getBoundingClientRect();
|
|
8708
|
+
let rect = caretRect;
|
|
8709
|
+
if (!rect || rect.top === 0 && rect.bottom === 0 && rect.left === 0) {
|
|
8710
|
+
const node = range.startContainer;
|
|
8711
|
+
const el = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
|
|
8712
|
+
if (el) rect = el.getBoundingClientRect();
|
|
8713
|
+
}
|
|
8714
|
+
if (!rect || rect.bottom === 0) return;
|
|
8715
|
+
let scrollEl = (_b = (_a = containerRef.current) == null ? void 0 : _a.parentElement) != null ? _b : null;
|
|
8716
|
+
while (scrollEl) {
|
|
8717
|
+
const style = window.getComputedStyle(scrollEl);
|
|
8718
|
+
const overflow = style.overflow + style.overflowY;
|
|
8719
|
+
if (/auto|scroll/.test(overflow)) break;
|
|
8720
|
+
scrollEl = scrollEl.parentElement;
|
|
8721
|
+
}
|
|
8722
|
+
const PADDING = 24;
|
|
8723
|
+
if (scrollEl) {
|
|
8724
|
+
const containerRect = scrollEl.getBoundingClientRect();
|
|
8725
|
+
if (rect.bottom > containerRect.bottom - PADDING) {
|
|
8726
|
+
scrollEl.scrollBy({ top: rect.bottom - containerRect.bottom + PADDING, behavior: "smooth" });
|
|
8727
|
+
} else if (rect.top < containerRect.top + PADDING) {
|
|
8728
|
+
scrollEl.scrollBy({ top: rect.top - containerRect.top - PADDING, behavior: "smooth" });
|
|
8802
8729
|
}
|
|
8803
|
-
|
|
8804
|
-
|
|
8805
|
-
|
|
8806
|
-
|
|
8807
|
-
|
|
8808
|
-
engine.dispatch(tr);
|
|
8809
|
-
return true;
|
|
8810
|
-
},
|
|
8811
|
-
"ArrowLeft": (engine) => {
|
|
8812
|
-
var _a;
|
|
8813
|
-
const state = engine.getState();
|
|
8814
|
-
const sel = state.selection;
|
|
8815
|
-
if (!sel) return false;
|
|
8816
|
-
const cellPos = findCellPosition(state.doc, sel.anchor.path);
|
|
8817
|
-
if (!cellPos) return false;
|
|
8818
|
-
const { tablePath, row, col } = cellPos;
|
|
8819
|
-
const cellPath = [...tablePath, row, col];
|
|
8820
|
-
const cellEl = getCellDOMElement(JSON.stringify(cellPath));
|
|
8821
|
-
if (!cellEl || !isCaretAtContainerStart(cellEl)) return false;
|
|
8822
|
-
const table = getNodeAtPath(state.doc, tablePath);
|
|
8823
|
-
const { cols } = getTableDimensions(table);
|
|
8824
|
-
let prevRow = row;
|
|
8825
|
-
let prevCol = col - 1;
|
|
8826
|
-
while (prevRow >= 0) {
|
|
8827
|
-
if (prevCol < 0) {
|
|
8828
|
-
prevCol = cols - 1;
|
|
8829
|
-
prevRow--;
|
|
8830
|
-
continue;
|
|
8831
|
-
}
|
|
8832
|
-
const c = getNodeAtPath(state.doc, [...tablePath, prevRow, prevCol]);
|
|
8833
|
-
if (!((_a = c == null ? void 0 : c.attrs) == null ? void 0 : _a.covered)) break;
|
|
8834
|
-
prevCol--;
|
|
8730
|
+
} else {
|
|
8731
|
+
if (rect.bottom > window.innerHeight - PADDING) {
|
|
8732
|
+
window.scrollBy({ top: rect.bottom - window.innerHeight + PADDING, behavior: "smooth" });
|
|
8733
|
+
} else if (rect.top < PADDING) {
|
|
8734
|
+
window.scrollBy({ top: rect.top - PADDING, behavior: "smooth" });
|
|
8835
8735
|
}
|
|
8836
|
-
|
|
8837
|
-
|
|
8838
|
-
|
|
8736
|
+
}
|
|
8737
|
+
}, []);
|
|
8738
|
+
const isComposingRef = useRef(false);
|
|
8739
|
+
const stateRef = useRef(engine.getState());
|
|
8740
|
+
const [selectedImagePath, setSelectedImagePath] = useState(null);
|
|
8741
|
+
const [findReplaceOpen, setFindReplaceOpen] = useState(false);
|
|
8742
|
+
const [findReplaceMode, setFindReplaceMode] = useState("find");
|
|
8743
|
+
const [linkPopupOpen, setLinkPopupOpen] = useState(false);
|
|
8744
|
+
const [isSourceMode, setIsSourceMode] = useState(false);
|
|
8745
|
+
const [sourceHTML, setSourceHTML] = useState("");
|
|
8746
|
+
const [editorHeight, setEditorHeight] = useState(null);
|
|
8747
|
+
const editorRootRef = useRef(null);
|
|
8748
|
+
const resizeDragRef = useRef(null);
|
|
8749
|
+
const [linkTooltip, setLinkTooltip] = useState(null);
|
|
8750
|
+
const linkTooltipTimerRef = useRef(null);
|
|
8751
|
+
const handleToggleSource = useCallback(() => {
|
|
8752
|
+
if (!isSourceMode) {
|
|
8753
|
+
const html = htmlSerializer.serialize(engine.getState().doc);
|
|
8754
|
+
setSourceHTML(prettyPrintHTML2(html));
|
|
8755
|
+
setIsSourceMode(true);
|
|
8756
|
+
} else {
|
|
8757
|
+
const newDoc = htmlSerializer.deserialize(sourceHTML);
|
|
8839
8758
|
const tr = createTransaction();
|
|
8840
|
-
tr.steps.push(
|
|
8759
|
+
tr.steps.push(tr_replaceDoc(newDoc));
|
|
8841
8760
|
engine.dispatch(tr);
|
|
8842
|
-
|
|
8843
|
-
}
|
|
8761
|
+
setIsSourceMode(false);
|
|
8762
|
+
}
|
|
8763
|
+
}, [isSourceMode, sourceHTML, engine]);
|
|
8764
|
+
useEffect(() => {
|
|
8765
|
+
const onMouseMove = (e) => {
|
|
8766
|
+
const drag = resizeDragRef.current;
|
|
8767
|
+
if (!drag) return;
|
|
8768
|
+
const newHeight = Math.max(120, drag.startHeight + (e.clientY - drag.startY));
|
|
8769
|
+
setEditorHeight(newHeight);
|
|
8770
|
+
};
|
|
8771
|
+
const onMouseUp = () => {
|
|
8772
|
+
resizeDragRef.current = null;
|
|
8773
|
+
};
|
|
8774
|
+
document.addEventListener("mousemove", onMouseMove);
|
|
8775
|
+
document.addEventListener("mouseup", onMouseUp);
|
|
8776
|
+
return () => {
|
|
8777
|
+
document.removeEventListener("mousemove", onMouseMove);
|
|
8778
|
+
document.removeEventListener("mouseup", onMouseUp);
|
|
8779
|
+
};
|
|
8780
|
+
}, []);
|
|
8781
|
+
const [tableContextMenu, setTableContextMenu] = useState(null);
|
|
8782
|
+
const [tableSelection, setTableSelection] = useState(null);
|
|
8783
|
+
const cellDragRef = useRef(null);
|
|
8784
|
+
const colResizeRef = useRef(null);
|
|
8785
|
+
const state = useEditorState(engine);
|
|
8786
|
+
const inTableCellPos = state.selection ? findCellPosition(state.doc, state.selection.anchor.path) : null;
|
|
8787
|
+
useLayoutEffect(() => {
|
|
8788
|
+
stateRef.current = state;
|
|
8789
|
+
}, [state]);
|
|
8790
|
+
useLayoutEffect(() => {
|
|
8791
|
+
const container = containerRef.current;
|
|
8792
|
+
if (!container) return;
|
|
8793
|
+
isRenderingRef.current = true;
|
|
8794
|
+
renderDocument(state.doc, container);
|
|
8795
|
+
if (!container.contains(document.activeElement)) {
|
|
8796
|
+
container.focus({ preventScroll: true });
|
|
8797
|
+
}
|
|
8798
|
+
if (state.selection) {
|
|
8799
|
+
restoreSelection(container, state.selection);
|
|
8800
|
+
scrollCaretIntoView();
|
|
8801
|
+
}
|
|
8802
|
+
isRenderingRef.current = false;
|
|
8803
|
+
if (onHTMLChange) {
|
|
8804
|
+
onHTMLChange(htmlSerializer.serialize(state.doc));
|
|
8805
|
+
}
|
|
8806
|
+
if (onJSONChange) {
|
|
8807
|
+
onJSONChange(jsonSerializer.serialize(state.doc));
|
|
8808
|
+
}
|
|
8809
|
+
}, [state.doc]);
|
|
8810
|
+
useLayoutEffect(() => {
|
|
8811
|
+
const container = containerRef.current;
|
|
8812
|
+
if (!container || !state.selection) return;
|
|
8813
|
+
restoreSelection(container, state.selection);
|
|
8814
|
+
scrollCaretIntoView();
|
|
8815
|
+
}, [state.selection]);
|
|
8816
|
+
useEffect(() => {
|
|
8817
|
+
const container = containerRef.current;
|
|
8818
|
+
if (!container || readOnly) return;
|
|
8819
|
+
return attachClipboardHandlers(container, engine);
|
|
8820
|
+
}, [engine, readOnly]);
|
|
8821
|
+
useEffect(() => {
|
|
8822
|
+
const container = containerRef.current;
|
|
8823
|
+
if (!container) return;
|
|
8824
|
+
container.querySelectorAll(".editor-cell-selected").forEach((el) => {
|
|
8825
|
+
el.classList.remove("editor-cell-selected");
|
|
8826
|
+
});
|
|
8827
|
+
if (!tableSelection) return;
|
|
8828
|
+
const { tablePath, anchorCell, focusCell } = tableSelection;
|
|
8829
|
+
const minRow = Math.min(anchorCell[0], focusCell[0]);
|
|
8830
|
+
const maxRow = Math.max(anchorCell[0], focusCell[0]);
|
|
8831
|
+
const minCol = Math.min(anchorCell[1], focusCell[1]);
|
|
8832
|
+
const maxCol = Math.max(anchorCell[1], focusCell[1]);
|
|
8833
|
+
for (let r = minRow; r <= maxRow; r++) {
|
|
8834
|
+
for (let c = minCol; c <= maxCol; c++) {
|
|
8835
|
+
const cellPath = [...tablePath, r, c];
|
|
8836
|
+
const td = container.querySelector(
|
|
8837
|
+
`[data-block-path="${JSON.stringify(cellPath)}"]`
|
|
8838
|
+
);
|
|
8839
|
+
if (td) td.classList.add("editor-cell-selected");
|
|
8840
|
+
}
|
|
8841
|
+
}
|
|
8842
|
+
}, [tableSelection, state.doc]);
|
|
8843
|
+
useEffect(() => {
|
|
8844
|
+
const container = containerRef.current;
|
|
8845
|
+
if (!container) return;
|
|
8846
|
+
container.querySelectorAll(".editor-cell-active").forEach((el) => {
|
|
8847
|
+
el.classList.remove("editor-cell-active");
|
|
8848
|
+
});
|
|
8849
|
+
if (!inTableCellPos) {
|
|
8850
|
+
setTableSelection(null);
|
|
8851
|
+
return;
|
|
8852
|
+
}
|
|
8853
|
+
if (tableSelection) {
|
|
8854
|
+
const { anchorCell, focusCell, tablePath } = tableSelection;
|
|
8855
|
+
const sameTable = JSON.stringify(tablePath) === JSON.stringify(inTableCellPos.tablePath);
|
|
8856
|
+
const singleCell = anchorCell[0] === focusCell[0] && anchorCell[1] === focusCell[1];
|
|
8857
|
+
if (!sameTable || singleCell) {
|
|
8858
|
+
setTableSelection(null);
|
|
8859
|
+
}
|
|
8860
|
+
}
|
|
8861
|
+
const cellPath = [...inTableCellPos.tablePath, inTableCellPos.row, inTableCellPos.col];
|
|
8862
|
+
const td = container.querySelector(
|
|
8863
|
+
`[data-block-path="${JSON.stringify(cellPath)}"]`
|
|
8864
|
+
);
|
|
8865
|
+
if (td) td.classList.add("editor-cell-active");
|
|
8866
|
+
}, [inTableCellPos, state.doc]);
|
|
8867
|
+
useEffect(() => {
|
|
8868
|
+
const onSelectionChange = () => {
|
|
8869
|
+
if (isRenderingRef.current) return;
|
|
8870
|
+
const container = containerRef.current;
|
|
8871
|
+
if (!container) return;
|
|
8872
|
+
if (!isSelectionInContainer(container)) return;
|
|
8873
|
+
const captured = captureSelection(container);
|
|
8874
|
+
if (!captured) return;
|
|
8875
|
+
const currentSelection = engine.getState().selection;
|
|
8876
|
+
if (currentSelection && JSON.stringify(currentSelection.anchor) === JSON.stringify(captured.anchor) && JSON.stringify(currentSelection.focus) === JSON.stringify(captured.focus)) {
|
|
8877
|
+
return;
|
|
8878
|
+
}
|
|
8879
|
+
const tr = createTransaction();
|
|
8880
|
+
tr.steps.push(tr_setSelection(captured));
|
|
8881
|
+
engine.dispatch(tr);
|
|
8882
|
+
};
|
|
8883
|
+
document.addEventListener(
|
|
8884
|
+
"selectionchange",
|
|
8885
|
+
onSelectionChange
|
|
8886
|
+
);
|
|
8887
|
+
return () => {
|
|
8888
|
+
document.removeEventListener(
|
|
8889
|
+
"selectionchange",
|
|
8890
|
+
onSelectionChange
|
|
8891
|
+
);
|
|
8892
|
+
};
|
|
8893
|
+
}, [engine]);
|
|
8894
|
+
const handleFocus = useCallback(() => {
|
|
8895
|
+
const currentState = engine.getState();
|
|
8896
|
+
if (!currentState.selection) {
|
|
8897
|
+
const tr = createTransaction();
|
|
8898
|
+
tr.steps.push(
|
|
8899
|
+
tr_setSelection(
|
|
8900
|
+
makeCollapsedSelection({ path: [0], offset: 0 })
|
|
8901
|
+
)
|
|
8902
|
+
);
|
|
8903
|
+
engine.dispatch(tr);
|
|
8904
|
+
}
|
|
8905
|
+
onFocusProp == null ? void 0 : onFocusProp();
|
|
8906
|
+
}, [engine, onFocusProp]);
|
|
8907
|
+
const handleBlur = useCallback(() => {
|
|
8908
|
+
onBlurProp == null ? void 0 : onBlurProp();
|
|
8909
|
+
}, [onBlurProp]);
|
|
8910
|
+
const handleKeyDown = useCallback(
|
|
8911
|
+
(e) => {
|
|
8912
|
+
var _a, _b, _c;
|
|
8913
|
+
if (readOnly) return;
|
|
8914
|
+
if ((e.ctrlKey || e.metaKey) && e.key === "f") {
|
|
8915
|
+
e.preventDefault();
|
|
8916
|
+
setFindReplaceMode("find");
|
|
8917
|
+
setFindReplaceOpen(true);
|
|
8918
|
+
return;
|
|
8919
|
+
}
|
|
8920
|
+
if ((e.ctrlKey || e.metaKey) && e.key === "h") {
|
|
8921
|
+
e.preventDefault();
|
|
8922
|
+
setFindReplaceMode("replace");
|
|
8923
|
+
setFindReplaceOpen(true);
|
|
8924
|
+
return;
|
|
8925
|
+
}
|
|
8926
|
+
if ((e.ctrlKey || e.metaKey) && e.key === "k") {
|
|
8927
|
+
e.preventDefault();
|
|
8928
|
+
setLinkPopupOpen(true);
|
|
8929
|
+
onOpenLinkPopup == null ? void 0 : onOpenLinkPopup();
|
|
8930
|
+
return;
|
|
8931
|
+
}
|
|
8932
|
+
if ((e.ctrlKey || e.metaKey) && (e.key === "a" || e.key === "A")) {
|
|
8933
|
+
e.preventDefault();
|
|
8934
|
+
const container = containerRef.current;
|
|
8935
|
+
if (container) {
|
|
8936
|
+
const spans = container.querySelectorAll("[data-path]");
|
|
8937
|
+
const first = spans[0];
|
|
8938
|
+
const last = spans[spans.length - 1];
|
|
8939
|
+
if (first && last) {
|
|
8940
|
+
const sel = window.getSelection();
|
|
8941
|
+
const range = document.createRange();
|
|
8942
|
+
range.setStart((_a = leafTextNode2(first, "first")) != null ? _a : first, 0);
|
|
8943
|
+
const lastLeaf = leafTextNode2(last, "last");
|
|
8944
|
+
range.setEnd(lastLeaf != null ? lastLeaf : last, (_c = (_b = lastLeaf == null ? void 0 : lastLeaf.textContent) == null ? void 0 : _b.length) != null ? _c : 0);
|
|
8945
|
+
sel == null ? void 0 : sel.removeAllRanges();
|
|
8946
|
+
sel == null ? void 0 : sel.addRange(range);
|
|
8947
|
+
}
|
|
8948
|
+
}
|
|
8949
|
+
return;
|
|
8950
|
+
}
|
|
8951
|
+
if (selectedImagePath && (e.key === "Delete" || e.key === "Backspace")) {
|
|
8952
|
+
e.preventDefault();
|
|
8953
|
+
deleteImageAtPath(selectedImagePath)(engine);
|
|
8954
|
+
setSelectedImagePath(null);
|
|
8955
|
+
return;
|
|
8956
|
+
}
|
|
8957
|
+
if (selectedImagePath) setSelectedImagePath(null);
|
|
8958
|
+
if (e.key === "Tab") {
|
|
8959
|
+
const { doc, selection: sel } = engine.getState();
|
|
8960
|
+
if (sel) {
|
|
8961
|
+
const bp = findContentBlockPath(doc, sel.anchor.path);
|
|
8962
|
+
const block = bp ? getNodeAtPath(doc, bp) : null;
|
|
8963
|
+
if ((block == null ? void 0 : block.type) === "code_block") {
|
|
8964
|
+
e.preventDefault();
|
|
8965
|
+
insertText(" ")(engine);
|
|
8966
|
+
return;
|
|
8967
|
+
}
|
|
8968
|
+
if ((block == null ? void 0 : block.type) === "list_item") {
|
|
8969
|
+
e.preventDefault();
|
|
8970
|
+
if (e.shiftKey) {
|
|
8971
|
+
outdentListItem(engine);
|
|
8972
|
+
} else {
|
|
8973
|
+
indentListItem(engine);
|
|
8974
|
+
}
|
|
8975
|
+
return;
|
|
8976
|
+
}
|
|
8977
|
+
if (findCellPosition(doc, sel.anchor.path)) {
|
|
8978
|
+
e.preventDefault();
|
|
8979
|
+
}
|
|
8980
|
+
}
|
|
8981
|
+
}
|
|
8982
|
+
const handled = engine.handleKeyDown(
|
|
8983
|
+
e.nativeEvent
|
|
8984
|
+
);
|
|
8985
|
+
if (handled) return;
|
|
8986
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
8987
|
+
e.preventDefault();
|
|
8988
|
+
handleEnter(engine);
|
|
8989
|
+
return;
|
|
8990
|
+
}
|
|
8991
|
+
if (e.key === "Backspace") {
|
|
8992
|
+
e.preventDefault();
|
|
8993
|
+
handleBackspace(engine);
|
|
8994
|
+
return;
|
|
8995
|
+
}
|
|
8996
|
+
if (e.key === "Delete") {
|
|
8997
|
+
e.preventDefault();
|
|
8998
|
+
handleDelete(engine);
|
|
8999
|
+
return;
|
|
9000
|
+
}
|
|
9001
|
+
if (e.key === "Enter" && e.shiftKey) {
|
|
9002
|
+
e.preventDefault();
|
|
9003
|
+
insertText("\n")(engine);
|
|
9004
|
+
return;
|
|
9005
|
+
}
|
|
9006
|
+
},
|
|
9007
|
+
[engine, readOnly, onOpenLinkPopup, selectedImagePath]
|
|
9008
|
+
);
|
|
9009
|
+
useEffect(() => {
|
|
9010
|
+
const container = containerRef.current;
|
|
9011
|
+
if (!container || readOnly) return;
|
|
9012
|
+
const onBeforeInput = (e) => {
|
|
9013
|
+
if (e.isComposing || isComposingRef.current) return;
|
|
9014
|
+
switch (e.inputType) {
|
|
9015
|
+
case "insertText":
|
|
9016
|
+
case "insertReplacementText": {
|
|
9017
|
+
if (!e.data) return;
|
|
9018
|
+
e.preventDefault();
|
|
9019
|
+
insertText(e.data)(engine);
|
|
9020
|
+
return;
|
|
9021
|
+
}
|
|
9022
|
+
case "insertParagraph": {
|
|
9023
|
+
e.preventDefault();
|
|
9024
|
+
handleEnter(engine);
|
|
9025
|
+
return;
|
|
9026
|
+
}
|
|
9027
|
+
case "insertLineBreak": {
|
|
9028
|
+
e.preventDefault();
|
|
9029
|
+
insertText("\n")(engine);
|
|
9030
|
+
return;
|
|
9031
|
+
}
|
|
9032
|
+
case "deleteContentBackward":
|
|
9033
|
+
case "deleteWordBackward":
|
|
9034
|
+
case "deleteSoftLineBackward": {
|
|
9035
|
+
e.preventDefault();
|
|
9036
|
+
handleBackspace(engine);
|
|
9037
|
+
return;
|
|
9038
|
+
}
|
|
9039
|
+
case "insertFromPaste":
|
|
9040
|
+
case "insertFromDrop":
|
|
9041
|
+
return;
|
|
9042
|
+
default:
|
|
9043
|
+
e.preventDefault();
|
|
9044
|
+
}
|
|
9045
|
+
};
|
|
9046
|
+
container.addEventListener("beforeinput", onBeforeInput);
|
|
9047
|
+
return () => container.removeEventListener("beforeinput", onBeforeInput);
|
|
9048
|
+
}, [engine, readOnly]);
|
|
9049
|
+
useEffect(() => {
|
|
9050
|
+
const container = containerRef.current;
|
|
9051
|
+
if (!container || readOnly) return;
|
|
9052
|
+
const onCompositionStart = () => {
|
|
9053
|
+
isComposingRef.current = true;
|
|
9054
|
+
};
|
|
9055
|
+
const onCompositionEnd = (e) => {
|
|
9056
|
+
isComposingRef.current = false;
|
|
9057
|
+
if (e.data) insertText(e.data)(engine);
|
|
9058
|
+
};
|
|
9059
|
+
container.addEventListener("compositionstart", onCompositionStart);
|
|
9060
|
+
container.addEventListener("compositionend", onCompositionEnd);
|
|
9061
|
+
return () => {
|
|
9062
|
+
container.removeEventListener("compositionstart", onCompositionStart);
|
|
9063
|
+
container.removeEventListener("compositionend", onCompositionEnd);
|
|
9064
|
+
};
|
|
9065
|
+
}, [engine, readOnly]);
|
|
9066
|
+
useEffect(() => {
|
|
9067
|
+
const container = containerRef.current;
|
|
9068
|
+
if (!container) return;
|
|
9069
|
+
const onMouseOver = (e) => {
|
|
9070
|
+
var _a;
|
|
9071
|
+
const anchor = e.target.closest("a.editor-link");
|
|
9072
|
+
if (!anchor) return;
|
|
9073
|
+
const href = (_a = anchor.getAttribute("href")) != null ? _a : "";
|
|
9074
|
+
if (!href) return;
|
|
9075
|
+
if (linkTooltipTimerRef.current) clearTimeout(linkTooltipTimerRef.current);
|
|
9076
|
+
linkTooltipTimerRef.current = setTimeout(() => {
|
|
9077
|
+
const rect = anchor.getBoundingClientRect();
|
|
9078
|
+
const containerRect = container.getBoundingClientRect();
|
|
9079
|
+
setLinkTooltip({
|
|
9080
|
+
href,
|
|
9081
|
+
x: rect.left - containerRect.left,
|
|
9082
|
+
y: rect.bottom - containerRect.top + 6
|
|
9083
|
+
});
|
|
9084
|
+
}, 200);
|
|
9085
|
+
};
|
|
9086
|
+
const onMouseOut = (e) => {
|
|
9087
|
+
const anchor = e.target.closest("a.editor-link");
|
|
9088
|
+
if (!anchor) return;
|
|
9089
|
+
if (linkTooltipTimerRef.current) clearTimeout(linkTooltipTimerRef.current);
|
|
9090
|
+
setLinkTooltip(null);
|
|
9091
|
+
};
|
|
9092
|
+
const onClick = (e) => {
|
|
9093
|
+
if (!(e.ctrlKey || e.metaKey)) return;
|
|
9094
|
+
const anchor = e.target.closest("a.editor-link");
|
|
9095
|
+
if (!anchor) return;
|
|
9096
|
+
e.preventDefault();
|
|
9097
|
+
const href = anchor.getAttribute("href");
|
|
9098
|
+
if (href) window.open(href, "_blank", "noopener,noreferrer");
|
|
9099
|
+
};
|
|
9100
|
+
container.addEventListener("mouseover", onMouseOver);
|
|
9101
|
+
container.addEventListener("mouseout", onMouseOut);
|
|
9102
|
+
container.addEventListener("click", onClick);
|
|
9103
|
+
return () => {
|
|
9104
|
+
container.removeEventListener("mouseover", onMouseOver);
|
|
9105
|
+
container.removeEventListener("mouseout", onMouseOut);
|
|
9106
|
+
container.removeEventListener("click", onClick);
|
|
9107
|
+
if (linkTooltipTimerRef.current) clearTimeout(linkTooltipTimerRef.current);
|
|
9108
|
+
};
|
|
9109
|
+
}, []);
|
|
9110
|
+
useEffect(() => {
|
|
9111
|
+
const onMouseMove = (e) => {
|
|
9112
|
+
const drag = colResizeRef.current;
|
|
9113
|
+
if (!drag) return;
|
|
9114
|
+
const delta = e.clientX - drag.startX;
|
|
9115
|
+
const newWidth = Math.max(40, drag.startWidth + delta);
|
|
9116
|
+
const container = containerRef.current;
|
|
9117
|
+
if (container) {
|
|
9118
|
+
const colEl = container.querySelector(
|
|
9119
|
+
`col[data-col-index="${drag.colIndex}"]`
|
|
9120
|
+
);
|
|
9121
|
+
if (colEl) colEl.style.width = `${newWidth}px`;
|
|
9122
|
+
}
|
|
9123
|
+
};
|
|
9124
|
+
const onMouseUp = (e) => {
|
|
9125
|
+
const drag = colResizeRef.current;
|
|
9126
|
+
if (!drag) return;
|
|
9127
|
+
const delta = e.clientX - drag.startX;
|
|
9128
|
+
const newWidth = Math.max(40, drag.startWidth + delta);
|
|
9129
|
+
colResizeRef.current = null;
|
|
9130
|
+
setColumnWidth(drag.tablePath, drag.colIndex, newWidth)(engine);
|
|
9131
|
+
};
|
|
9132
|
+
document.addEventListener("mousemove", onMouseMove);
|
|
9133
|
+
document.addEventListener("mouseup", onMouseUp);
|
|
9134
|
+
return () => {
|
|
9135
|
+
document.removeEventListener("mousemove", onMouseMove);
|
|
9136
|
+
document.removeEventListener("mouseup", onMouseUp);
|
|
9137
|
+
};
|
|
9138
|
+
}, [engine]);
|
|
9139
|
+
useEffect(() => {
|
|
9140
|
+
const onMouseMove = (e) => {
|
|
9141
|
+
var _a, _b;
|
|
9142
|
+
const drag = cellDragRef.current;
|
|
9143
|
+
if (!drag) return;
|
|
9144
|
+
const td = e.target.closest("[data-cell-row]");
|
|
9145
|
+
if (!td) return;
|
|
9146
|
+
const row = parseInt((_a = td.dataset.cellRow) != null ? _a : "0");
|
|
9147
|
+
const col = parseInt((_b = td.dataset.cellCol) != null ? _b : "0");
|
|
9148
|
+
const tdTablePath = td.dataset.cellTablePath ? JSON.parse(td.dataset.cellTablePath) : null;
|
|
9149
|
+
if (!tdTablePath || JSON.stringify(tdTablePath) !== JSON.stringify(drag.tablePath)) return;
|
|
9150
|
+
setTableSelection({
|
|
9151
|
+
tablePath: drag.tablePath,
|
|
9152
|
+
anchorCell: [drag.startRow, drag.startCol],
|
|
9153
|
+
focusCell: [row, col]
|
|
9154
|
+
});
|
|
9155
|
+
};
|
|
9156
|
+
const onMouseUp = () => {
|
|
9157
|
+
cellDragRef.current = null;
|
|
9158
|
+
};
|
|
9159
|
+
document.addEventListener("mousemove", onMouseMove);
|
|
9160
|
+
document.addEventListener("mouseup", onMouseUp);
|
|
9161
|
+
return () => {
|
|
9162
|
+
document.removeEventListener("mousemove", onMouseMove);
|
|
9163
|
+
document.removeEventListener("mouseup", onMouseUp);
|
|
9164
|
+
};
|
|
9165
|
+
}, []);
|
|
9166
|
+
const handleContextMenu = useCallback(
|
|
9167
|
+
(e) => {
|
|
9168
|
+
var _a, _b, _c, _d, _e, _f;
|
|
9169
|
+
const td = e.target.closest("[data-cell-row]");
|
|
9170
|
+
if (!td) return;
|
|
9171
|
+
e.preventDefault();
|
|
9172
|
+
const row = parseInt((_a = td.dataset.cellRow) != null ? _a : "0");
|
|
9173
|
+
const col = parseInt((_b = td.dataset.cellCol) != null ? _b : "0");
|
|
9174
|
+
const tablePath = td.dataset.cellTablePath ? JSON.parse(td.dataset.cellTablePath) : null;
|
|
9175
|
+
if (!tablePath) return;
|
|
9176
|
+
const cellNode = getNodeAtPath(
|
|
9177
|
+
engine.getState().doc,
|
|
9178
|
+
[...tablePath, row, col]
|
|
9179
|
+
);
|
|
9180
|
+
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);
|
|
9181
|
+
setTableContextMenu({ x: e.clientX, y: e.clientY, tablePath, row, col, isMerged });
|
|
9182
|
+
},
|
|
9183
|
+
[engine]
|
|
9184
|
+
);
|
|
9185
|
+
const handleMouseDown = useCallback(
|
|
9186
|
+
(e) => {
|
|
9187
|
+
var _a, _b, _c, _d;
|
|
9188
|
+
if (readOnly) return;
|
|
9189
|
+
const target = e.target;
|
|
9190
|
+
if (target.dataset.resizeImagePath) {
|
|
9191
|
+
e.preventDefault();
|
|
9192
|
+
const imagePath = JSON.parse(target.dataset.resizeImagePath);
|
|
9193
|
+
const corner = (_a = target.dataset.resizeImagePos) != null ? _a : "se";
|
|
9194
|
+
const container = containerRef.current;
|
|
9195
|
+
let startWidth = 300;
|
|
9196
|
+
if (container) {
|
|
9197
|
+
const fig2 = container.querySelector(`[data-image-path="${JSON.stringify(imagePath)}"]`);
|
|
9198
|
+
const img = fig2 == null ? void 0 : fig2.querySelector("img");
|
|
9199
|
+
if (img) startWidth = img.getBoundingClientRect().width || 300;
|
|
9200
|
+
}
|
|
9201
|
+
imageResizeRef.current = {
|
|
9202
|
+
imagePath,
|
|
9203
|
+
corner,
|
|
9204
|
+
startX: e.clientX,
|
|
9205
|
+
startY: e.clientY,
|
|
9206
|
+
startWidth,
|
|
9207
|
+
startHeight: 0
|
|
9208
|
+
};
|
|
9209
|
+
return;
|
|
9210
|
+
}
|
|
9211
|
+
const fig = target.closest("[data-image-path]");
|
|
9212
|
+
if (fig == null ? void 0 : fig.dataset.imagePath) {
|
|
9213
|
+
e.preventDefault();
|
|
9214
|
+
setSelectedImagePath(JSON.parse(fig.dataset.imagePath));
|
|
9215
|
+
return;
|
|
9216
|
+
}
|
|
9217
|
+
setSelectedImagePath(null);
|
|
9218
|
+
if (target.dataset.resizeTable) {
|
|
9219
|
+
e.preventDefault();
|
|
9220
|
+
const tablePath = JSON.parse(target.dataset.resizeTable);
|
|
9221
|
+
const colIndex = parseInt((_b = target.dataset.resizeCol) != null ? _b : "0");
|
|
9222
|
+
const container = containerRef.current;
|
|
9223
|
+
let startWidth = 120;
|
|
9224
|
+
if (container) {
|
|
9225
|
+
const colEl = container.querySelector(
|
|
9226
|
+
`col[data-col-index="${colIndex}"]`
|
|
9227
|
+
);
|
|
9228
|
+
if (colEl) startWidth = colEl.getBoundingClientRect().width || 120;
|
|
9229
|
+
}
|
|
9230
|
+
colResizeRef.current = { tablePath, colIndex, startX: e.clientX, startWidth };
|
|
9231
|
+
return;
|
|
9232
|
+
}
|
|
9233
|
+
const td = target.closest("[data-cell-row]");
|
|
9234
|
+
if (!td) setTableSelection(null);
|
|
9235
|
+
if (td && !readOnly) {
|
|
9236
|
+
const row = parseInt((_c = td.dataset.cellRow) != null ? _c : "0");
|
|
9237
|
+
const col = parseInt((_d = td.dataset.cellCol) != null ? _d : "0");
|
|
9238
|
+
const tdTablePath = td.dataset.cellTablePath ? JSON.parse(td.dataset.cellTablePath) : null;
|
|
9239
|
+
if (tdTablePath) {
|
|
9240
|
+
cellDragRef.current = { tablePath: tdTablePath, startRow: row, startCol: col };
|
|
9241
|
+
setTableSelection({
|
|
9242
|
+
tablePath: tdTablePath,
|
|
9243
|
+
anchorCell: [row, col],
|
|
9244
|
+
focusCell: [row, col]
|
|
9245
|
+
});
|
|
9246
|
+
}
|
|
9247
|
+
}
|
|
9248
|
+
const checkTarget = target.dataset.checkPath ? target : target.closest("[data-check-path]");
|
|
9249
|
+
if (checkTarget == null ? void 0 : checkTarget.dataset.checkPath) {
|
|
9250
|
+
e.preventDefault();
|
|
9251
|
+
toggleCheckItemAt(JSON.parse(checkTarget.dataset.checkPath))(engine);
|
|
9252
|
+
}
|
|
9253
|
+
},
|
|
9254
|
+
[engine, readOnly]
|
|
9255
|
+
);
|
|
9256
|
+
const handleClick = useCallback(() => {
|
|
9257
|
+
const container = containerRef.current;
|
|
9258
|
+
if (!container) return;
|
|
9259
|
+
const selection = captureSelection(container);
|
|
9260
|
+
if (!selection) return;
|
|
9261
|
+
const tr = createTransaction();
|
|
9262
|
+
tr.steps.push(
|
|
9263
|
+
tr_setSelection(selection)
|
|
9264
|
+
);
|
|
9265
|
+
engine.dispatch(tr);
|
|
9266
|
+
}, [engine]);
|
|
9267
|
+
const imageResizeRef = useRef(null);
|
|
9268
|
+
useEffect(() => {
|
|
9269
|
+
const onMouseMove = (e) => {
|
|
9270
|
+
const drag = imageResizeRef.current;
|
|
9271
|
+
if (!drag) return;
|
|
9272
|
+
const deltaX = e.clientX - drag.startX;
|
|
9273
|
+
const isRight = drag.corner === "ne" || drag.corner === "se";
|
|
9274
|
+
const newWidth = Math.max(60, drag.startWidth + (isRight ? deltaX : -deltaX));
|
|
9275
|
+
const container = containerRef.current;
|
|
9276
|
+
if (container) {
|
|
9277
|
+
const fig = container.querySelector(
|
|
9278
|
+
`[data-image-path="${JSON.stringify(drag.imagePath)}"]`
|
|
9279
|
+
);
|
|
9280
|
+
const img = fig == null ? void 0 : fig.querySelector("img");
|
|
9281
|
+
if (img) img.style.width = `${newWidth}px`;
|
|
9282
|
+
}
|
|
9283
|
+
};
|
|
9284
|
+
const onMouseUp = (e) => {
|
|
9285
|
+
const drag = imageResizeRef.current;
|
|
9286
|
+
if (!drag) return;
|
|
9287
|
+
const deltaX = e.clientX - drag.startX;
|
|
9288
|
+
const isRight = drag.corner === "ne" || drag.corner === "se";
|
|
9289
|
+
const newWidth = Math.max(60, drag.startWidth + (isRight ? deltaX : -deltaX));
|
|
9290
|
+
imageResizeRef.current = null;
|
|
9291
|
+
setImageAttr(drag.imagePath, { width: newWidth })(engine);
|
|
9292
|
+
};
|
|
9293
|
+
document.addEventListener("mousemove", onMouseMove);
|
|
9294
|
+
document.addEventListener("mouseup", onMouseUp);
|
|
9295
|
+
return () => {
|
|
9296
|
+
document.removeEventListener("mousemove", onMouseMove);
|
|
9297
|
+
document.removeEventListener("mouseup", onMouseUp);
|
|
9298
|
+
};
|
|
9299
|
+
}, [engine]);
|
|
9300
|
+
useEffect(() => {
|
|
9301
|
+
const container = containerRef.current;
|
|
9302
|
+
if (!container) return;
|
|
9303
|
+
container.querySelectorAll("[data-image-path]").forEach((el) => {
|
|
9304
|
+
el.removeAttribute("data-selected");
|
|
9305
|
+
});
|
|
9306
|
+
if (selectedImagePath) {
|
|
9307
|
+
const fig = container.querySelector(
|
|
9308
|
+
`[data-image-path="${JSON.stringify(selectedImagePath)}"]`
|
|
9309
|
+
);
|
|
9310
|
+
if (fig) fig.setAttribute("data-selected", "true");
|
|
9311
|
+
}
|
|
9312
|
+
}, [selectedImagePath, state.doc]);
|
|
9313
|
+
useEffect(() => {
|
|
9314
|
+
const container = containerRef.current;
|
|
9315
|
+
if (!container || readOnly) return;
|
|
9316
|
+
const onDragOver = (e) => {
|
|
9317
|
+
var _a;
|
|
9318
|
+
if ((_a = e.dataTransfer) == null ? void 0 : _a.types.includes("Files")) e.preventDefault();
|
|
9319
|
+
};
|
|
9320
|
+
const onDrop = (e) => {
|
|
9321
|
+
var _a;
|
|
9322
|
+
e.preventDefault();
|
|
9323
|
+
const file = (_a = e.dataTransfer) == null ? void 0 : _a.files[0];
|
|
9324
|
+
if (!file || !file.type.startsWith("image/")) return;
|
|
9325
|
+
const blobUrl = URL.createObjectURL(file);
|
|
9326
|
+
insertImage(blobUrl, file.name.replace(/\.[^.]+$/, ""))(engine);
|
|
9327
|
+
};
|
|
9328
|
+
container.addEventListener("dragover", onDragOver);
|
|
9329
|
+
container.addEventListener("drop", onDrop);
|
|
9330
|
+
return () => {
|
|
9331
|
+
container.removeEventListener("dragover", onDragOver);
|
|
9332
|
+
container.removeEventListener("drop", onDrop);
|
|
9333
|
+
};
|
|
9334
|
+
}, [engine, readOnly]);
|
|
9335
|
+
const isVisualEmpty = state.doc.children.length === 1 && state.doc.children[0].type === "paragraph" && state.doc.children[0].children.length === 0;
|
|
9336
|
+
return /* @__PURE__ */ jsxs(
|
|
9337
|
+
"div",
|
|
9338
|
+
{
|
|
9339
|
+
ref: editorRootRef,
|
|
9340
|
+
className: `editor-root relative flex flex-col ${className}`,
|
|
9341
|
+
style: editorHeight ? { minHeight: editorHeight } : void 0,
|
|
9342
|
+
children: [
|
|
9343
|
+
!readOnly && !hideToolbar && /* @__PURE__ */ jsx(
|
|
9344
|
+
Toolbar,
|
|
9345
|
+
{
|
|
9346
|
+
engine,
|
|
9347
|
+
onFindReplace: (mode) => {
|
|
9348
|
+
setFindReplaceMode(mode);
|
|
9349
|
+
setFindReplaceOpen(true);
|
|
9350
|
+
},
|
|
9351
|
+
linkPopupOpen,
|
|
9352
|
+
onLinkPopupClose: () => setLinkPopupOpen(false),
|
|
9353
|
+
isSourceMode,
|
|
9354
|
+
onToggleSource: handleToggleSource,
|
|
9355
|
+
toolbarConfig
|
|
9356
|
+
}
|
|
9357
|
+
),
|
|
9358
|
+
/* @__PURE__ */ jsx("div", { className: "relative", children: isSourceMode ? /* @__PURE__ */ jsx(
|
|
9359
|
+
"textarea",
|
|
9360
|
+
{
|
|
9361
|
+
"aria-label": "HTML source editor",
|
|
9362
|
+
value: sourceHTML,
|
|
9363
|
+
onChange: (e) => {
|
|
9364
|
+
setSourceHTML(e.target.value);
|
|
9365
|
+
const t = e.target;
|
|
9366
|
+
t.style.height = "auto";
|
|
9367
|
+
t.style.height = t.scrollHeight + "px";
|
|
9368
|
+
},
|
|
9369
|
+
ref: (el) => {
|
|
9370
|
+
if (el) {
|
|
9371
|
+
el.style.height = "auto";
|
|
9372
|
+
el.style.height = el.scrollHeight + "px";
|
|
9373
|
+
}
|
|
9374
|
+
},
|
|
9375
|
+
spellCheck: false,
|
|
9376
|
+
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"
|
|
9377
|
+
}
|
|
9378
|
+
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9379
|
+
isVisualEmpty && /* @__PURE__ */ jsx(
|
|
9380
|
+
"div",
|
|
9381
|
+
{
|
|
9382
|
+
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 \r\n ",
|
|
9383
|
+
"aria-hidden": "true",
|
|
9384
|
+
children: placeholder
|
|
9385
|
+
}
|
|
9386
|
+
),
|
|
9387
|
+
/* @__PURE__ */ jsx(
|
|
9388
|
+
"div",
|
|
9389
|
+
{
|
|
9390
|
+
ref: containerRef,
|
|
9391
|
+
contentEditable: !readOnly,
|
|
9392
|
+
suppressContentEditableWarning: true,
|
|
9393
|
+
role: "textbox",
|
|
9394
|
+
"aria-multiline": "true",
|
|
9395
|
+
"aria-label": "Rich text editor",
|
|
9396
|
+
spellCheck: true,
|
|
9397
|
+
onMouseDown: handleMouseDown,
|
|
9398
|
+
onContextMenu: handleContextMenu,
|
|
9399
|
+
onFocus: handleFocus,
|
|
9400
|
+
onBlur: handleBlur,
|
|
9401
|
+
onClick: handleClick,
|
|
9402
|
+
onKeyDown: handleKeyDown,
|
|
9403
|
+
className: [
|
|
9404
|
+
"editor-canvas",
|
|
9405
|
+
"min-h-[300px]",
|
|
9406
|
+
"px-6",
|
|
9407
|
+
"py-4",
|
|
9408
|
+
"outline-none",
|
|
9409
|
+
"text-base",
|
|
9410
|
+
"leading-relaxed",
|
|
9411
|
+
"text-gray-900",
|
|
9412
|
+
"",
|
|
9413
|
+
readOnly ? "cursor-default" : "cursor-text"
|
|
9414
|
+
].join(" ")
|
|
9415
|
+
}
|
|
9416
|
+
),
|
|
9417
|
+
linkTooltip && /* @__PURE__ */ jsxs(
|
|
9418
|
+
"div",
|
|
9419
|
+
{
|
|
9420
|
+
role: "tooltip",
|
|
9421
|
+
style: { left: linkTooltip.x, top: linkTooltip.y },
|
|
9422
|
+
className: "absolute z-50 pointer-events-none flex items-center gap-1.5 bg-gray-900 text-white text-xs px-2.5 py-1.5 rounded shadow-lg max-w-xs",
|
|
9423
|
+
children: [
|
|
9424
|
+
/* @__PURE__ */ jsxs("svg", { width: "11", height: "11", viewBox: "0 0 16 16", fill: "none", className: "shrink-0 opacity-70", "aria-hidden": "true", children: [
|
|
9425
|
+
/* @__PURE__ */ jsx("path", { d: "M6.5 3.5H4A2.5 2.5 0 0 0 4 8.5h2", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round" }),
|
|
9426
|
+
/* @__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" }),
|
|
9427
|
+
/* @__PURE__ */ jsx("path", { d: "M5.5 6h5", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round" })
|
|
9428
|
+
] }),
|
|
9429
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: linkTooltip.href }),
|
|
9430
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 opacity-50 ml-1", children: "Ctrl+click to open" })
|
|
9431
|
+
]
|
|
9432
|
+
}
|
|
9433
|
+
)
|
|
9434
|
+
] }) }),
|
|
9435
|
+
inTableCellPos && !readOnly && /* @__PURE__ */ jsx(
|
|
9436
|
+
TableToolbar,
|
|
9437
|
+
{
|
|
9438
|
+
engine,
|
|
9439
|
+
tablePath: inTableCellPos.tablePath,
|
|
9440
|
+
cellPos: { row: inTableCellPos.row, col: inTableCellPos.col },
|
|
9441
|
+
tableSelection,
|
|
9442
|
+
editorContainer: containerRef
|
|
9443
|
+
}
|
|
9444
|
+
),
|
|
9445
|
+
tableContextMenu && /* @__PURE__ */ jsx(
|
|
9446
|
+
TableContextMenu,
|
|
9447
|
+
{
|
|
9448
|
+
x: tableContextMenu.x,
|
|
9449
|
+
y: tableContextMenu.y,
|
|
9450
|
+
tablePath: tableContextMenu.tablePath,
|
|
9451
|
+
row: tableContextMenu.row,
|
|
9452
|
+
col: tableContextMenu.col,
|
|
9453
|
+
isMerged: tableContextMenu.isMerged,
|
|
9454
|
+
tableSelection,
|
|
9455
|
+
engine,
|
|
9456
|
+
onClose: () => setTableContextMenu(null)
|
|
9457
|
+
}
|
|
9458
|
+
),
|
|
9459
|
+
findReplaceOpen && /* @__PURE__ */ jsx(
|
|
9460
|
+
FindReplaceModal,
|
|
9461
|
+
{
|
|
9462
|
+
engine,
|
|
9463
|
+
editorContainer: containerRef,
|
|
9464
|
+
initialMode: findReplaceMode,
|
|
9465
|
+
onClose: () => setFindReplaceOpen(false)
|
|
9466
|
+
}
|
|
9467
|
+
),
|
|
9468
|
+
selectedImagePath && !readOnly && /* @__PURE__ */ jsx(
|
|
9469
|
+
ImageToolbar,
|
|
9470
|
+
{
|
|
9471
|
+
engine,
|
|
9472
|
+
imagePath: selectedImagePath,
|
|
9473
|
+
editorContainer: containerRef,
|
|
9474
|
+
onClose: () => setSelectedImagePath(null)
|
|
9475
|
+
}
|
|
9476
|
+
),
|
|
9477
|
+
!readOnly && /* @__PURE__ */ jsx(
|
|
9478
|
+
"div",
|
|
9479
|
+
{
|
|
9480
|
+
title: "Drag to resize",
|
|
9481
|
+
onMouseDown: (e) => {
|
|
9482
|
+
e.preventDefault();
|
|
9483
|
+
const rootEl = editorRootRef.current;
|
|
9484
|
+
if (!rootEl) return;
|
|
9485
|
+
resizeDragRef.current = {
|
|
9486
|
+
startY: e.clientY,
|
|
9487
|
+
startHeight: rootEl.getBoundingClientRect().height
|
|
9488
|
+
};
|
|
9489
|
+
},
|
|
9490
|
+
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",
|
|
9491
|
+
"aria-hidden": "true",
|
|
9492
|
+
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" }) })
|
|
9493
|
+
}
|
|
9494
|
+
)
|
|
9495
|
+
]
|
|
9496
|
+
}
|
|
9497
|
+
);
|
|
9498
|
+
}
|
|
9499
|
+
var BLOCK_TAGS2 = "p|h[1-6]|ul|ol|li|blockquote|pre|figure|table|thead|tbody|tr|th|td";
|
|
9500
|
+
var _OPEN_ONLY_RE2 = new RegExp(`^<(${BLOCK_TAGS2})(?:\\s[^>]*)?>$`, "i");
|
|
9501
|
+
var _CLOSE_ONLY_RE2 = new RegExp(`^<\\/(${BLOCK_TAGS2})>$`, "i");
|
|
9502
|
+
var _HR_RE2 = /^<hr(\s[^>]*)?\/?>$/i;
|
|
9503
|
+
function leafTextNode2(el, end) {
|
|
9504
|
+
let node = el;
|
|
9505
|
+
while (node.hasChildNodes()) {
|
|
9506
|
+
node = end === "first" ? node.firstChild : node.lastChild;
|
|
9507
|
+
}
|
|
9508
|
+
return node.nodeType === Node.TEXT_NODE ? node : null;
|
|
9509
|
+
}
|
|
9510
|
+
function prettyPrintHTML2(html) {
|
|
9511
|
+
const blockOpen = new RegExp(`(<(?:${BLOCK_TAGS2})(?:\\s[^>]*)?>)`, "gi");
|
|
9512
|
+
const blockClose = new RegExp(`(<\\/(?:${BLOCK_TAGS2})>)`, "gi");
|
|
9513
|
+
const spread = html.replace(blockOpen, "\n$1").replace(blockClose, "$1\n").replace(/<hr(\s[^>]*)?\/?>(\s*)/gi, "\n<hr />\n");
|
|
9514
|
+
let depth = 0;
|
|
9515
|
+
const result = [];
|
|
9516
|
+
for (const raw of spread.split("\n")) {
|
|
9517
|
+
const line = raw.trim();
|
|
9518
|
+
if (!line) continue;
|
|
9519
|
+
if (_HR_RE2.test(line)) {
|
|
9520
|
+
result.push(" ".repeat(depth) + line);
|
|
9521
|
+
continue;
|
|
9522
|
+
}
|
|
9523
|
+
if (_CLOSE_ONLY_RE2.test(line)) {
|
|
9524
|
+
depth = Math.max(0, depth - 1);
|
|
9525
|
+
result.push(" ".repeat(depth) + line);
|
|
9526
|
+
continue;
|
|
9527
|
+
}
|
|
9528
|
+
if (_OPEN_ONLY_RE2.test(line)) {
|
|
9529
|
+
result.push(" ".repeat(depth) + line);
|
|
9530
|
+
depth++;
|
|
9531
|
+
continue;
|
|
9532
|
+
}
|
|
9533
|
+
result.push(" ".repeat(depth) + line);
|
|
9534
|
+
}
|
|
9535
|
+
return result.join("\n");
|
|
9536
|
+
}
|
|
9537
|
+
|
|
9538
|
+
// src/editor/table/TablePlugin.ts
|
|
9539
|
+
function getCellDOMElement(cellPathJson) {
|
|
9540
|
+
return document.querySelector(`[data-block-path='${cellPathJson}']`);
|
|
9541
|
+
}
|
|
9542
|
+
function isCaretAtContainerStart(container) {
|
|
9543
|
+
const sel = window.getSelection();
|
|
9544
|
+
if (!sel || sel.rangeCount === 0 || !sel.isCollapsed) return false;
|
|
9545
|
+
const range = sel.getRangeAt(0);
|
|
9546
|
+
if (range.startOffset !== 0) return false;
|
|
9547
|
+
let node = range.startContainer;
|
|
9548
|
+
while (node && node !== container) {
|
|
9549
|
+
if (node.previousSibling) return false;
|
|
9550
|
+
node = node.parentNode;
|
|
9551
|
+
}
|
|
9552
|
+
return node === container;
|
|
9553
|
+
}
|
|
9554
|
+
function isCaretAtContainerEnd(container) {
|
|
9555
|
+
var _a, _b;
|
|
9556
|
+
const sel = window.getSelection();
|
|
9557
|
+
if (!sel || sel.rangeCount === 0 || !sel.isCollapsed) return false;
|
|
9558
|
+
const range = sel.getRangeAt(0);
|
|
9559
|
+
const endNode = range.endContainer;
|
|
9560
|
+
const endOffset = range.endOffset;
|
|
9561
|
+
const len = endNode.nodeType === Node.TEXT_NODE ? (_b = (_a = endNode.textContent) == null ? void 0 : _a.length) != null ? _b : 0 : endNode.childNodes.length;
|
|
9562
|
+
if (endOffset !== len) return false;
|
|
9563
|
+
let node = endNode;
|
|
9564
|
+
while (node && node !== container) {
|
|
9565
|
+
if (node.nextSibling) return false;
|
|
9566
|
+
node = node.parentNode;
|
|
9567
|
+
}
|
|
9568
|
+
return node === container;
|
|
9569
|
+
}
|
|
9570
|
+
function isCaretOnEdgeLine(container, direction) {
|
|
9571
|
+
const sel = window.getSelection();
|
|
9572
|
+
if (!sel || sel.rangeCount === 0 || !sel.isCollapsed) return false;
|
|
9573
|
+
const caretRange = sel.getRangeAt(0).cloneRange();
|
|
9574
|
+
caretRange.collapse(true);
|
|
9575
|
+
const caretRects = caretRange.getClientRects();
|
|
9576
|
+
if (!caretRects.length) return true;
|
|
9577
|
+
const caretRect = caretRects[0];
|
|
9578
|
+
const containerRect = container.getBoundingClientRect();
|
|
9579
|
+
const lineHeight = parseFloat(getComputedStyle(container).lineHeight) || 20;
|
|
9580
|
+
if (direction === "up") {
|
|
9581
|
+
return caretRect.top < containerRect.top + lineHeight;
|
|
9582
|
+
} else {
|
|
9583
|
+
return caretRect.bottom > containerRect.bottom - lineHeight;
|
|
9584
|
+
}
|
|
9585
|
+
}
|
|
9586
|
+
var TablePlugin = {
|
|
9587
|
+
name: "table",
|
|
9588
|
+
keyBindings: {
|
|
9589
|
+
Tab: (engine) => {
|
|
9590
|
+
var _a;
|
|
9591
|
+
const state = engine.getState();
|
|
9592
|
+
const sel = state.selection;
|
|
9593
|
+
if (!sel) return false;
|
|
9594
|
+
const cellPos = findCellPosition(state.doc, sel.anchor.path);
|
|
9595
|
+
if (!cellPos) return false;
|
|
9596
|
+
const { tablePath, row, col } = cellPos;
|
|
9597
|
+
const table = getNodeAtPath(state.doc, tablePath);
|
|
9598
|
+
const { rows, cols } = getTableDimensions(table);
|
|
9599
|
+
let nextRow = row;
|
|
9600
|
+
let nextCol = col + 1;
|
|
9601
|
+
while (nextRow < rows) {
|
|
9602
|
+
if (nextCol >= cols) {
|
|
9603
|
+
nextCol = 0;
|
|
9604
|
+
nextRow++;
|
|
9605
|
+
continue;
|
|
9606
|
+
}
|
|
9607
|
+
const c = getNodeAtPath(state.doc, [...tablePath, nextRow, nextCol]);
|
|
9608
|
+
if (!((_a = c == null ? void 0 : c.attrs) == null ? void 0 : _a.covered)) break;
|
|
9609
|
+
nextCol++;
|
|
9610
|
+
}
|
|
9611
|
+
if (nextRow >= rows) {
|
|
9612
|
+
const newTable = insertTableRowAfter(table, rows - 1);
|
|
9613
|
+
const newChildren = [...state.doc.children];
|
|
9614
|
+
newChildren[tablePath[0]] = newTable;
|
|
9615
|
+
const tempDoc = { type: "doc", children: newChildren };
|
|
9616
|
+
const tr2 = createTransaction();
|
|
9617
|
+
tr2.steps.push({ type: "delete_node", path: tablePath });
|
|
9618
|
+
tr2.steps.push({
|
|
9619
|
+
type: "insert_node",
|
|
9620
|
+
parentPath: tablePath.length > 1 ? tablePath.slice(0, -1) : [],
|
|
9621
|
+
index: tablePath[tablePath.length - 1],
|
|
9622
|
+
node: newTable
|
|
9623
|
+
});
|
|
9624
|
+
const pos2 = getCellFirstPosition(tempDoc, tablePath, rows, 0);
|
|
9625
|
+
if (pos2) tr2.steps.push(tr_setSelection(makeCollapsedSelection(pos2)));
|
|
9626
|
+
engine.dispatch(tr2);
|
|
9627
|
+
return true;
|
|
9628
|
+
}
|
|
9629
|
+
const pos = getCellFirstPosition(state.doc, tablePath, nextRow, nextCol);
|
|
9630
|
+
if (!pos) return false;
|
|
9631
|
+
const tr = createTransaction();
|
|
9632
|
+
tr.steps.push(tr_setSelection(makeCollapsedSelection(pos)));
|
|
9633
|
+
engine.dispatch(tr);
|
|
9634
|
+
return true;
|
|
9635
|
+
},
|
|
9636
|
+
"ArrowRight": (engine) => {
|
|
9637
|
+
var _a;
|
|
9638
|
+
const state = engine.getState();
|
|
9639
|
+
const sel = state.selection;
|
|
9640
|
+
if (!sel) return false;
|
|
9641
|
+
const cellPos = findCellPosition(state.doc, sel.anchor.path);
|
|
9642
|
+
if (!cellPos) return false;
|
|
9643
|
+
const { tablePath, row, col } = cellPos;
|
|
9644
|
+
const cellPath = [...tablePath, row, col];
|
|
9645
|
+
const cellEl = getCellDOMElement(JSON.stringify(cellPath));
|
|
9646
|
+
if (!cellEl || !isCaretAtContainerEnd(cellEl)) return false;
|
|
9647
|
+
const table = getNodeAtPath(state.doc, tablePath);
|
|
9648
|
+
const { rows, cols } = getTableDimensions(table);
|
|
9649
|
+
let nextRow = row;
|
|
9650
|
+
let nextCol = col + 1;
|
|
9651
|
+
while (nextRow < rows) {
|
|
9652
|
+
if (nextCol >= cols) {
|
|
9653
|
+
nextCol = 0;
|
|
9654
|
+
nextRow++;
|
|
9655
|
+
continue;
|
|
9656
|
+
}
|
|
9657
|
+
const c = getNodeAtPath(state.doc, [...tablePath, nextRow, nextCol]);
|
|
9658
|
+
if (!((_a = c == null ? void 0 : c.attrs) == null ? void 0 : _a.covered)) break;
|
|
9659
|
+
nextCol++;
|
|
9660
|
+
}
|
|
9661
|
+
if (nextRow >= rows) return true;
|
|
9662
|
+
const pos = getCellFirstPosition(state.doc, tablePath, nextRow, nextCol);
|
|
9663
|
+
if (!pos) return false;
|
|
9664
|
+
const tr = createTransaction();
|
|
9665
|
+
tr.steps.push(tr_setSelection(makeCollapsedSelection(pos)));
|
|
9666
|
+
engine.dispatch(tr);
|
|
9667
|
+
return true;
|
|
9668
|
+
},
|
|
9669
|
+
"ArrowLeft": (engine) => {
|
|
9670
|
+
var _a;
|
|
9671
|
+
const state = engine.getState();
|
|
9672
|
+
const sel = state.selection;
|
|
9673
|
+
if (!sel) return false;
|
|
9674
|
+
const cellPos = findCellPosition(state.doc, sel.anchor.path);
|
|
9675
|
+
if (!cellPos) return false;
|
|
9676
|
+
const { tablePath, row, col } = cellPos;
|
|
9677
|
+
const cellPath = [...tablePath, row, col];
|
|
9678
|
+
const cellEl = getCellDOMElement(JSON.stringify(cellPath));
|
|
9679
|
+
if (!cellEl || !isCaretAtContainerStart(cellEl)) return false;
|
|
9680
|
+
const table = getNodeAtPath(state.doc, tablePath);
|
|
9681
|
+
const { cols } = getTableDimensions(table);
|
|
9682
|
+
let prevRow = row;
|
|
9683
|
+
let prevCol = col - 1;
|
|
9684
|
+
while (prevRow >= 0) {
|
|
9685
|
+
if (prevCol < 0) {
|
|
9686
|
+
prevCol = cols - 1;
|
|
9687
|
+
prevRow--;
|
|
9688
|
+
continue;
|
|
9689
|
+
}
|
|
9690
|
+
const c = getNodeAtPath(state.doc, [...tablePath, prevRow, prevCol]);
|
|
9691
|
+
if (!((_a = c == null ? void 0 : c.attrs) == null ? void 0 : _a.covered)) break;
|
|
9692
|
+
prevCol--;
|
|
9693
|
+
}
|
|
9694
|
+
if (prevRow < 0) return true;
|
|
9695
|
+
const pos = getCellLastPosition(state.doc, tablePath, prevRow, prevCol);
|
|
9696
|
+
if (!pos) return false;
|
|
9697
|
+
const tr = createTransaction();
|
|
9698
|
+
tr.steps.push(tr_setSelection(makeCollapsedSelection(pos)));
|
|
9699
|
+
engine.dispatch(tr);
|
|
9700
|
+
return true;
|
|
9701
|
+
},
|
|
8844
9702
|
"ArrowDown": (engine) => {
|
|
8845
9703
|
var _a;
|
|
8846
9704
|
const state = engine.getState();
|
|
@@ -9139,151 +9997,6 @@ function execCommand(engine, command, ...args) {
|
|
|
9139
9997
|
function registerCommand(name, fn) {
|
|
9140
9998
|
REGISTRY[name] = fn;
|
|
9141
9999
|
}
|
|
9142
|
-
var Editor = forwardRef(function Editor2(props, ref) {
|
|
9143
|
-
var _a;
|
|
9144
|
-
const {
|
|
9145
|
-
value,
|
|
9146
|
-
defaultValue,
|
|
9147
|
-
onChange,
|
|
9148
|
-
placeholder,
|
|
9149
|
-
readOnly = false,
|
|
9150
|
-
toolbar = DEFAULT_TOOLBAR,
|
|
9151
|
-
theme = "light",
|
|
9152
|
-
plugins,
|
|
9153
|
-
onFocus,
|
|
9154
|
-
onBlur,
|
|
9155
|
-
onReady,
|
|
9156
|
-
onUploadImage,
|
|
9157
|
-
className,
|
|
9158
|
-
style,
|
|
9159
|
-
minHeight,
|
|
9160
|
-
maxHeight
|
|
9161
|
-
} = props;
|
|
9162
|
-
const engine = useEditorEngine();
|
|
9163
|
-
const pluginsRegisteredRef = useRef(false);
|
|
9164
|
-
if (!pluginsRegisteredRef.current && (plugins == null ? void 0 : plugins.length)) {
|
|
9165
|
-
pluginsRegisteredRef.current = true;
|
|
9166
|
-
for (const p of plugins) {
|
|
9167
|
-
engine.registerPlugin(p);
|
|
9168
|
-
}
|
|
9169
|
-
}
|
|
9170
|
-
const initializedRef = useRef(false);
|
|
9171
|
-
useEffect(() => {
|
|
9172
|
-
if (initializedRef.current) return;
|
|
9173
|
-
initializedRef.current = true;
|
|
9174
|
-
const initHTML = value != null ? value : defaultValue;
|
|
9175
|
-
if (!initHTML) return;
|
|
9176
|
-
const doc = htmlSerializer.deserialize(initHTML);
|
|
9177
|
-
const tr = createTransaction();
|
|
9178
|
-
tr.steps.push(tr_replaceDoc(doc));
|
|
9179
|
-
engine.dispatch(tr);
|
|
9180
|
-
}, []);
|
|
9181
|
-
const lastEmittedRef = useRef("");
|
|
9182
|
-
useEffect(() => {
|
|
9183
|
-
if (value === void 0) return;
|
|
9184
|
-
if (value === lastEmittedRef.current) return;
|
|
9185
|
-
const currentHTML = htmlSerializer.serialize(engine.getState().doc);
|
|
9186
|
-
if (value === currentHTML) return;
|
|
9187
|
-
const doc = htmlSerializer.deserialize(value);
|
|
9188
|
-
const tr = createTransaction();
|
|
9189
|
-
tr.steps.push(tr_replaceDoc(doc));
|
|
9190
|
-
engine.dispatch(tr);
|
|
9191
|
-
}, [value, engine]);
|
|
9192
|
-
const handleHTMLChange = useCallback((html) => {
|
|
9193
|
-
lastEmittedRef.current = html;
|
|
9194
|
-
onChange == null ? void 0 : onChange(html);
|
|
9195
|
-
}, [onChange]);
|
|
9196
|
-
const rootRef = useRef(null);
|
|
9197
|
-
const api = useMemo(() => ({
|
|
9198
|
-
getHTML: () => htmlSerializer.serialize(engine.getState().doc),
|
|
9199
|
-
setHTML: (html) => {
|
|
9200
|
-
const doc = htmlSerializer.deserialize(html);
|
|
9201
|
-
const tr = createTransaction();
|
|
9202
|
-
tr.steps.push(tr_replaceDoc(doc));
|
|
9203
|
-
engine.dispatch(tr);
|
|
9204
|
-
},
|
|
9205
|
-
getJSON: () => engine.getState().doc,
|
|
9206
|
-
getMarkdown: () => markdownSerializer.serialize(engine.getState().doc),
|
|
9207
|
-
focus: () => {
|
|
9208
|
-
var _a2;
|
|
9209
|
-
const ce = (_a2 = rootRef.current) == null ? void 0 : _a2.querySelector("[contenteditable]");
|
|
9210
|
-
ce == null ? void 0 : ce.focus();
|
|
9211
|
-
},
|
|
9212
|
-
blur: () => {
|
|
9213
|
-
var _a2;
|
|
9214
|
-
const ce = (_a2 = rootRef.current) == null ? void 0 : _a2.querySelector("[contenteditable]");
|
|
9215
|
-
ce == null ? void 0 : ce.blur();
|
|
9216
|
-
},
|
|
9217
|
-
clear: () => {
|
|
9218
|
-
const tr = createTransaction();
|
|
9219
|
-
tr.steps.push(tr_replaceDoc(createEmptyDocument()));
|
|
9220
|
-
engine.dispatch(tr);
|
|
9221
|
-
},
|
|
9222
|
-
undo: () => {
|
|
9223
|
-
engine.handleKeyDown(
|
|
9224
|
-
new KeyboardEvent("keydown", { key: "z", ctrlKey: true, bubbles: true })
|
|
9225
|
-
);
|
|
9226
|
-
},
|
|
9227
|
-
redo: () => {
|
|
9228
|
-
engine.handleKeyDown(
|
|
9229
|
-
new KeyboardEvent("keydown", { key: "y", ctrlKey: true, bubbles: true })
|
|
9230
|
-
);
|
|
9231
|
-
},
|
|
9232
|
-
execCommand: (command, ...args) => {
|
|
9233
|
-
return execCommand(engine, command, ...args);
|
|
9234
|
-
},
|
|
9235
|
-
registerCommand: (name, fn) => {
|
|
9236
|
-
registerCommand(name, fn);
|
|
9237
|
-
},
|
|
9238
|
-
getEngine: () => engine
|
|
9239
|
-
}), [engine]);
|
|
9240
|
-
useImperativeHandle(ref, () => api, [api]);
|
|
9241
|
-
useEffect(() => {
|
|
9242
|
-
onReady == null ? void 0 : onReady(api);
|
|
9243
|
-
}, []);
|
|
9244
|
-
const themeMode = typeof theme === "string" ? theme : (_a = theme.mode) != null ? _a : "light";
|
|
9245
|
-
const themeTokenOverrides = typeof theme === "object" && theme.tokens ? theme.tokens : void 0;
|
|
9246
|
-
const rootStyle = useMemo(() => {
|
|
9247
|
-
const base = { ...style };
|
|
9248
|
-
if (minHeight !== void 0)
|
|
9249
|
-
base.minHeight = typeof minHeight === "number" ? `${minHeight}px` : minHeight;
|
|
9250
|
-
if (maxHeight !== void 0) {
|
|
9251
|
-
base.maxHeight = typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight;
|
|
9252
|
-
base.overflow = "auto";
|
|
9253
|
-
}
|
|
9254
|
-
if (themeTokenOverrides) {
|
|
9255
|
-
for (const [key, val] of Object.entries(themeTokenOverrides)) {
|
|
9256
|
-
const cssVar = `--editor-${key.replace(/([A-Z])/g, (m) => `-${m.toLowerCase()}`)}`;
|
|
9257
|
-
base[cssVar] = val;
|
|
9258
|
-
}
|
|
9259
|
-
}
|
|
9260
|
-
return base;
|
|
9261
|
-
}, [style, minHeight, maxHeight, themeTokenOverrides]);
|
|
9262
|
-
return /* @__PURE__ */ jsx(
|
|
9263
|
-
"div",
|
|
9264
|
-
{
|
|
9265
|
-
ref: rootRef,
|
|
9266
|
-
"data-editor-theme": themeMode,
|
|
9267
|
-
"data-traffica-editor": "",
|
|
9268
|
-
className,
|
|
9269
|
-
style: rootStyle,
|
|
9270
|
-
children: /* @__PURE__ */ jsx(
|
|
9271
|
-
EditorCore,
|
|
9272
|
-
{
|
|
9273
|
-
engine,
|
|
9274
|
-
placeholder,
|
|
9275
|
-
readOnly,
|
|
9276
|
-
hideToolbar: toolbar === false,
|
|
9277
|
-
toolbarConfig: toolbar !== false ? toolbar : void 0,
|
|
9278
|
-
onHTMLChange: handleHTMLChange,
|
|
9279
|
-
onFocus,
|
|
9280
|
-
onBlur,
|
|
9281
|
-
onUploadImage
|
|
9282
|
-
}
|
|
9283
|
-
)
|
|
9284
|
-
}
|
|
9285
|
-
);
|
|
9286
|
-
});
|
|
9287
10000
|
|
|
9288
10001
|
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 };
|
|
9289
10002
|
//# sourceMappingURL=index.mjs.map
|