@trafica/editor 1.0.44 → 1.0.46
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.js +217 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +217 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2203,6 +2203,10 @@ function mergeAtCursor(state, pastedDoc, sel) {
|
|
|
2203
2203
|
const lastIdx = mergedDoc.children.length - 1;
|
|
2204
2204
|
return { mergedDoc, cursorPos: endOfBlock(mergedDoc.children[lastIdx], lastIdx) };
|
|
2205
2205
|
}
|
|
2206
|
+
const LIST_CONTAINERS = /* @__PURE__ */ new Set(["bullet_list", "ordered_list", "check_list"]);
|
|
2207
|
+
if (LIST_CONTAINERS.has(currentBlock.type) && relPath.length > 0) {
|
|
2208
|
+
return mergeIntoListContainer(state, pasted, existingBlocks, blockIdx, currentBlock, relPath, charOffset);
|
|
2209
|
+
}
|
|
2206
2210
|
const [beforeChildren, afterChildren] = splitBlockChildren(currentBlock, relPath, charOffset);
|
|
2207
2211
|
let newBlocks;
|
|
2208
2212
|
let cursorBlockOffset;
|
|
@@ -2260,6 +2264,85 @@ function mergeAtCursor(state, pastedDoc, sel) {
|
|
|
2260
2264
|
cursorPos: cursorInBlock
|
|
2261
2265
|
};
|
|
2262
2266
|
}
|
|
2267
|
+
function mergeIntoListContainer(state, pasted, existingBlocks, blockIdx, listBlock, relPath, charOffset) {
|
|
2268
|
+
var _a, _b, _c, _d, _e;
|
|
2269
|
+
const itemIdx = relPath[0];
|
|
2270
|
+
const itemRelPath = relPath.slice(1);
|
|
2271
|
+
const listItems = listBlock.children;
|
|
2272
|
+
const currentItem = listItems[itemIdx];
|
|
2273
|
+
const isCheckList = listBlock.type === "check_list";
|
|
2274
|
+
const itemType = isCheckList ? "check_list_item" : "list_item";
|
|
2275
|
+
if (!currentItem) {
|
|
2276
|
+
const newBlocks = [...existingBlocks, ...pasted];
|
|
2277
|
+
const lastIdx = newBlocks.length - 1;
|
|
2278
|
+
return {
|
|
2279
|
+
mergedDoc: { ...state.doc, children: newBlocks },
|
|
2280
|
+
cursorPos: endOfBlock(newBlocks[lastIdx], lastIdx)
|
|
2281
|
+
};
|
|
2282
|
+
}
|
|
2283
|
+
const [beforeChildren, afterChildren] = splitBlockChildren(currentItem, itemRelPath, charOffset);
|
|
2284
|
+
const makeItem = (children) => ({
|
|
2285
|
+
type: itemType,
|
|
2286
|
+
attrs: isCheckList ? { checked: false } : {},
|
|
2287
|
+
children: children.length > 0 ? children : [{ type: "text", text: "", marks: [] }]
|
|
2288
|
+
});
|
|
2289
|
+
let newItems;
|
|
2290
|
+
let cursorItemIdx;
|
|
2291
|
+
if (pasted.length === 1) {
|
|
2292
|
+
const pastedChildren = (_a = pasted[0].children) != null ? _a : [];
|
|
2293
|
+
const merged = [...beforeChildren, ...pastedChildren, ...afterChildren];
|
|
2294
|
+
newItems = [
|
|
2295
|
+
...listItems.slice(0, itemIdx),
|
|
2296
|
+
{ ...currentItem, children: merged.length > 0 ? merged : [{ type: "text", text: "", marks: [] }] },
|
|
2297
|
+
...listItems.slice(itemIdx + 1)
|
|
2298
|
+
];
|
|
2299
|
+
cursorItemIdx = itemIdx;
|
|
2300
|
+
} else {
|
|
2301
|
+
const firstPasted = pasted[0];
|
|
2302
|
+
const lastPasted = pasted[pasted.length - 1];
|
|
2303
|
+
const middlePasted = pasted.slice(1, -1);
|
|
2304
|
+
const firstItem = { ...currentItem, children: [...beforeChildren, ...(_b = firstPasted.children) != null ? _b : []] };
|
|
2305
|
+
const lastItem = makeItem([...(_c = lastPasted.children) != null ? _c : [], ...afterChildren]);
|
|
2306
|
+
const midItems = middlePasted.map((p) => {
|
|
2307
|
+
var _a2;
|
|
2308
|
+
return makeItem((_a2 = p.children) != null ? _a2 : []);
|
|
2309
|
+
});
|
|
2310
|
+
newItems = [
|
|
2311
|
+
...listItems.slice(0, itemIdx),
|
|
2312
|
+
firstItem,
|
|
2313
|
+
...midItems,
|
|
2314
|
+
lastItem,
|
|
2315
|
+
...listItems.slice(itemIdx + 1)
|
|
2316
|
+
];
|
|
2317
|
+
cursorItemIdx = itemIdx + 1 + midItems.length;
|
|
2318
|
+
}
|
|
2319
|
+
const newList = { ...listBlock, children: newItems };
|
|
2320
|
+
const mergedBlocks = [
|
|
2321
|
+
...existingBlocks.slice(0, blockIdx),
|
|
2322
|
+
newList,
|
|
2323
|
+
...existingBlocks.slice(blockIdx + 1)
|
|
2324
|
+
];
|
|
2325
|
+
const targetItem = newItems[cursorItemIdx];
|
|
2326
|
+
let cursorPos;
|
|
2327
|
+
if (pasted.length === 1) {
|
|
2328
|
+
const pastedChildren = (_d = pasted[0].children) != null ? _d : [];
|
|
2329
|
+
const cursorChildIdx = beforeChildren.length + pastedChildren.length;
|
|
2330
|
+
const nodeBefore = targetItem == null ? void 0 : targetItem.children[cursorChildIdx - 1];
|
|
2331
|
+
cursorPos = nodeBefore && isTextNode(nodeBefore) ? { path: [blockIdx, cursorItemIdx, cursorChildIdx - 1], offset: nodeBefore.text.length } : { path: [blockIdx, cursorItemIdx], offset: 0 };
|
|
2332
|
+
} else {
|
|
2333
|
+
const lpChildren = (_e = pasted[pasted.length - 1].children) != null ? _e : [];
|
|
2334
|
+
if (lpChildren.length > 0) {
|
|
2335
|
+
const lastNode = lpChildren[lpChildren.length - 1];
|
|
2336
|
+
cursorPos = {
|
|
2337
|
+
path: [blockIdx, cursorItemIdx, lpChildren.length - 1],
|
|
2338
|
+
offset: isTextNode(lastNode) ? lastNode.text.length : 0
|
|
2339
|
+
};
|
|
2340
|
+
} else {
|
|
2341
|
+
cursorPos = { path: [blockIdx, cursorItemIdx], offset: 0 };
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2344
|
+
return { mergedDoc: { ...state.doc, children: mergedBlocks }, cursorPos };
|
|
2345
|
+
}
|
|
2263
2346
|
function splitBlockChildren(block, relPath, offset) {
|
|
2264
2347
|
const children = block.children;
|
|
2265
2348
|
if (children.length === 0) return [[], []];
|
|
@@ -7953,6 +8036,7 @@ function EditorCore({
|
|
|
7953
8036
|
const isComposingRef = react.useRef(false);
|
|
7954
8037
|
const stateRef = react.useRef(engine.getState());
|
|
7955
8038
|
const [selectedImagePath, setSelectedImagePath] = react.useState(null);
|
|
8039
|
+
const [imageDragState, setImageDragState] = react.useState(null);
|
|
7956
8040
|
const [findReplaceOpen, setFindReplaceOpen] = react.useState(false);
|
|
7957
8041
|
const [findReplaceMode, setFindReplaceMode] = react.useState("find");
|
|
7958
8042
|
const [linkPopupOpen, setLinkPopupOpen] = react.useState(false);
|
|
@@ -8433,7 +8517,21 @@ function EditorCore({
|
|
|
8433
8517
|
const fig = target.closest("[data-image-path]");
|
|
8434
8518
|
if (fig == null ? void 0 : fig.dataset.imagePath) {
|
|
8435
8519
|
e.preventDefault();
|
|
8436
|
-
|
|
8520
|
+
const path = JSON.parse(fig.dataset.imagePath);
|
|
8521
|
+
setSelectedImagePath(path);
|
|
8522
|
+
if (path.length === 1) {
|
|
8523
|
+
const imgEl = fig.querySelector("img");
|
|
8524
|
+
const rect = imgEl == null ? void 0 : imgEl.getBoundingClientRect();
|
|
8525
|
+
imageDragRef.current = {
|
|
8526
|
+
imagePath: path,
|
|
8527
|
+
startX: e.clientX,
|
|
8528
|
+
startY: e.clientY,
|
|
8529
|
+
active: false,
|
|
8530
|
+
ghostW: Math.min((rect == null ? void 0 : rect.width) || 300, 320),
|
|
8531
|
+
ghostH: Math.min((rect == null ? void 0 : rect.height) || 200, 200),
|
|
8532
|
+
ghostSrc: (imgEl == null ? void 0 : imgEl.src) || ""
|
|
8533
|
+
};
|
|
8534
|
+
}
|
|
8437
8535
|
return;
|
|
8438
8536
|
}
|
|
8439
8537
|
setSelectedImagePath(null);
|
|
@@ -8486,6 +8584,7 @@ function EditorCore({
|
|
|
8486
8584
|
engine.dispatch(tr);
|
|
8487
8585
|
}, [engine]);
|
|
8488
8586
|
const imageResizeRef = react.useRef(null);
|
|
8587
|
+
const imageDragRef = react.useRef(null);
|
|
8489
8588
|
react.useEffect(() => {
|
|
8490
8589
|
const onMouseMove = (e) => {
|
|
8491
8590
|
const drag = imageResizeRef.current;
|
|
@@ -8518,6 +8617,48 @@ function EditorCore({
|
|
|
8518
8617
|
document.removeEventListener("mouseup", onMouseUp);
|
|
8519
8618
|
};
|
|
8520
8619
|
}, [engine]);
|
|
8620
|
+
react.useEffect(() => {
|
|
8621
|
+
const DRAG_THRESHOLD = 6;
|
|
8622
|
+
const onMouseMove = (e) => {
|
|
8623
|
+
const drag = imageDragRef.current;
|
|
8624
|
+
if (!drag) return;
|
|
8625
|
+
const dx = e.clientX - drag.startX;
|
|
8626
|
+
const dy = e.clientY - drag.startY;
|
|
8627
|
+
if (!drag.active && Math.sqrt(dx * dx + dy * dy) < DRAG_THRESHOLD) return;
|
|
8628
|
+
drag.active = true;
|
|
8629
|
+
const container = containerRef.current;
|
|
8630
|
+
const { dropIndex, indicatorClientY } = getImageDropTarget(container, e.clientY, drag.imagePath);
|
|
8631
|
+
setImageDragState({
|
|
8632
|
+
ghostX: e.clientX,
|
|
8633
|
+
ghostY: e.clientY,
|
|
8634
|
+
ghostW: drag.ghostW,
|
|
8635
|
+
ghostH: drag.ghostH,
|
|
8636
|
+
ghostSrc: drag.ghostSrc,
|
|
8637
|
+
dropIndicatorClientY: indicatorClientY,
|
|
8638
|
+
dropIndex,
|
|
8639
|
+
imagePath: drag.imagePath
|
|
8640
|
+
});
|
|
8641
|
+
};
|
|
8642
|
+
const onMouseUp = (e) => {
|
|
8643
|
+
const drag = imageDragRef.current;
|
|
8644
|
+
if (!drag) return;
|
|
8645
|
+
imageDragRef.current = null;
|
|
8646
|
+
if (!drag.active) {
|
|
8647
|
+
setImageDragState(null);
|
|
8648
|
+
return;
|
|
8649
|
+
}
|
|
8650
|
+
const container = containerRef.current;
|
|
8651
|
+
const { dropIndex } = getImageDropTarget(container, e.clientY, drag.imagePath);
|
|
8652
|
+
setImageDragState(null);
|
|
8653
|
+
moveImageInDoc(drag.imagePath, dropIndex, engine);
|
|
8654
|
+
};
|
|
8655
|
+
document.addEventListener("mousemove", onMouseMove);
|
|
8656
|
+
document.addEventListener("mouseup", onMouseUp);
|
|
8657
|
+
return () => {
|
|
8658
|
+
document.removeEventListener("mousemove", onMouseMove);
|
|
8659
|
+
document.removeEventListener("mouseup", onMouseUp);
|
|
8660
|
+
};
|
|
8661
|
+
}, [engine]);
|
|
8521
8662
|
react.useEffect(() => {
|
|
8522
8663
|
const container = containerRef.current;
|
|
8523
8664
|
if (!container) return;
|
|
@@ -8635,6 +8776,19 @@ function EditorCore({
|
|
|
8635
8776
|
].join(" ")
|
|
8636
8777
|
}
|
|
8637
8778
|
),
|
|
8779
|
+
imageDragState && (() => {
|
|
8780
|
+
const container = containerRef.current;
|
|
8781
|
+
if (!container) return null;
|
|
8782
|
+
const cr = container.getBoundingClientRect();
|
|
8783
|
+
const relY = imageDragState.dropIndicatorClientY - cr.top + container.scrollTop;
|
|
8784
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8785
|
+
"div",
|
|
8786
|
+
{
|
|
8787
|
+
className: "editor-image-drop-indicator",
|
|
8788
|
+
style: { top: relY }
|
|
8789
|
+
}
|
|
8790
|
+
);
|
|
8791
|
+
})(),
|
|
8638
8792
|
linkTooltip && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
8639
8793
|
"div",
|
|
8640
8794
|
{
|
|
@@ -8695,6 +8849,26 @@ function EditorCore({
|
|
|
8695
8849
|
onClose: () => setSelectedImagePath(null)
|
|
8696
8850
|
}
|
|
8697
8851
|
),
|
|
8852
|
+
imageDragState && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8853
|
+
"div",
|
|
8854
|
+
{
|
|
8855
|
+
className: "editor-image-drag-ghost",
|
|
8856
|
+
style: {
|
|
8857
|
+
left: imageDragState.ghostX,
|
|
8858
|
+
top: imageDragState.ghostY,
|
|
8859
|
+
width: imageDragState.ghostW,
|
|
8860
|
+
height: imageDragState.ghostH
|
|
8861
|
+
},
|
|
8862
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
8863
|
+
"img",
|
|
8864
|
+
{
|
|
8865
|
+
src: imageDragState.ghostSrc,
|
|
8866
|
+
alt: "",
|
|
8867
|
+
style: { width: "100%", height: "100%", objectFit: "contain", display: "block" }
|
|
8868
|
+
}
|
|
8869
|
+
)
|
|
8870
|
+
}
|
|
8871
|
+
),
|
|
8698
8872
|
!readOnly && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8699
8873
|
"div",
|
|
8700
8874
|
{
|
|
@@ -8717,6 +8891,46 @@ function EditorCore({
|
|
|
8717
8891
|
}
|
|
8718
8892
|
);
|
|
8719
8893
|
}
|
|
8894
|
+
function getImageDropTarget(container, clientY, imagePath) {
|
|
8895
|
+
const fallback = { dropIndex: imagePath[0], indicatorClientY: clientY };
|
|
8896
|
+
if (!container) return fallback;
|
|
8897
|
+
const blocks = Array.from(
|
|
8898
|
+
container.querySelectorAll(":scope > [data-block-path]")
|
|
8899
|
+
);
|
|
8900
|
+
if (blocks.length === 0) return fallback;
|
|
8901
|
+
const gaps = [];
|
|
8902
|
+
gaps.push({ y: blocks[0].getBoundingClientRect().top, index: 0 });
|
|
8903
|
+
for (let i = 0; i < blocks.length - 1; i++) {
|
|
8904
|
+
const bottom = blocks[i].getBoundingClientRect().bottom;
|
|
8905
|
+
const top = blocks[i + 1].getBoundingClientRect().top;
|
|
8906
|
+
gaps.push({ y: (bottom + top) / 2, index: i + 1 });
|
|
8907
|
+
}
|
|
8908
|
+
gaps.push({ y: blocks[blocks.length - 1].getBoundingClientRect().bottom, index: blocks.length });
|
|
8909
|
+
let best = gaps[0];
|
|
8910
|
+
let bestDist = Math.abs(clientY - best.y);
|
|
8911
|
+
for (const gap of gaps) {
|
|
8912
|
+
const dist = Math.abs(clientY - gap.y);
|
|
8913
|
+
if (dist < bestDist) {
|
|
8914
|
+
bestDist = dist;
|
|
8915
|
+
best = gap;
|
|
8916
|
+
}
|
|
8917
|
+
}
|
|
8918
|
+
return { dropIndex: best.index, indicatorClientY: best.y };
|
|
8919
|
+
}
|
|
8920
|
+
function moveImageInDoc(imagePath, targetIndex, engine) {
|
|
8921
|
+
const state = engine.getState();
|
|
8922
|
+
const imageNode = getNodeAtPath(state.doc, imagePath);
|
|
8923
|
+
if (!imageNode) return;
|
|
8924
|
+
const oldIndex = imagePath[imagePath.length - 1];
|
|
8925
|
+
const parentPath = imagePath.slice(0, -1);
|
|
8926
|
+
let adj = targetIndex > oldIndex ? targetIndex - 1 : targetIndex;
|
|
8927
|
+
adj = Math.max(0, Math.min(state.doc.children.length - 1, adj));
|
|
8928
|
+
if (adj === oldIndex) return;
|
|
8929
|
+
const tr = createTransaction();
|
|
8930
|
+
tr.steps.push({ type: "delete_node", path: imagePath });
|
|
8931
|
+
tr.steps.push({ type: "insert_node", parentPath, index: adj, node: imageNode });
|
|
8932
|
+
engine.dispatch(tr);
|
|
8933
|
+
}
|
|
8720
8934
|
var BLOCK_TAGS = "p|h[1-6]|ul|ol|li|blockquote|pre|figure|table|thead|tbody|tr|th|td";
|
|
8721
8935
|
var _OPEN_ONLY_RE = new RegExp(`^<(${BLOCK_TAGS})(?:\\s[^>]*)?>$`, "i");
|
|
8722
8936
|
var _CLOSE_ONLY_RE = new RegExp(`^<\\/(${BLOCK_TAGS})>$`, "i");
|
|
@@ -9068,7 +9282,9 @@ ${text}
|
|
|
9068
9282
|
}
|
|
9069
9283
|
}
|
|
9070
9284
|
function serializeMDInline(children) {
|
|
9285
|
+
if (!children) return "";
|
|
9071
9286
|
return children.map((child) => {
|
|
9287
|
+
if (!child) return "";
|
|
9072
9288
|
if (!isTextNode(child)) return serializeMDBlock(child, 0);
|
|
9073
9289
|
return serializeMDText(child);
|
|
9074
9290
|
}).join("");
|