@trafica/editor 1.0.45 → 1.0.47

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 CHANGED
@@ -8036,6 +8036,7 @@ function EditorCore({
8036
8036
  const isComposingRef = react.useRef(false);
8037
8037
  const stateRef = react.useRef(engine.getState());
8038
8038
  const [selectedImagePath, setSelectedImagePath] = react.useState(null);
8039
+ const [imageDragState, setImageDragState] = react.useState(null);
8039
8040
  const [findReplaceOpen, setFindReplaceOpen] = react.useState(false);
8040
8041
  const [findReplaceMode, setFindReplaceMode] = react.useState("find");
8041
8042
  const [linkPopupOpen, setLinkPopupOpen] = react.useState(false);
@@ -8516,7 +8517,19 @@ function EditorCore({
8516
8517
  const fig = target.closest("[data-image-path]");
8517
8518
  if (fig == null ? void 0 : fig.dataset.imagePath) {
8518
8519
  e.preventDefault();
8519
- setSelectedImagePath(JSON.parse(fig.dataset.imagePath));
8520
+ const path = JSON.parse(fig.dataset.imagePath);
8521
+ setSelectedImagePath(path);
8522
+ const imgEl = fig.querySelector("img");
8523
+ const rect = imgEl == null ? void 0 : imgEl.getBoundingClientRect();
8524
+ imageDragRef.current = {
8525
+ imagePath: path,
8526
+ startX: e.clientX,
8527
+ startY: e.clientY,
8528
+ active: false,
8529
+ ghostW: Math.min((rect == null ? void 0 : rect.width) || 300, 320),
8530
+ ghostH: Math.min((rect == null ? void 0 : rect.height) || 200, 200),
8531
+ ghostSrc: (imgEl == null ? void 0 : imgEl.src) || ""
8532
+ };
8520
8533
  return;
8521
8534
  }
8522
8535
  setSelectedImagePath(null);
@@ -8569,6 +8582,7 @@ function EditorCore({
8569
8582
  engine.dispatch(tr);
8570
8583
  }, [engine]);
8571
8584
  const imageResizeRef = react.useRef(null);
8585
+ const imageDragRef = react.useRef(null);
8572
8586
  react.useEffect(() => {
8573
8587
  const onMouseMove = (e) => {
8574
8588
  const drag = imageResizeRef.current;
@@ -8601,6 +8615,48 @@ function EditorCore({
8601
8615
  document.removeEventListener("mouseup", onMouseUp);
8602
8616
  };
8603
8617
  }, [engine]);
8618
+ react.useEffect(() => {
8619
+ const DRAG_THRESHOLD = 6;
8620
+ const onMouseMove = (e) => {
8621
+ const drag = imageDragRef.current;
8622
+ if (!drag) return;
8623
+ const dx = e.clientX - drag.startX;
8624
+ const dy = e.clientY - drag.startY;
8625
+ if (!drag.active && Math.sqrt(dx * dx + dy * dy) < DRAG_THRESHOLD) return;
8626
+ drag.active = true;
8627
+ const container = containerRef.current;
8628
+ const { dropIndex, indicatorClientY } = getImageDropTarget(container, e.clientY, drag.imagePath);
8629
+ setImageDragState({
8630
+ ghostX: e.clientX,
8631
+ ghostY: e.clientY,
8632
+ ghostW: drag.ghostW,
8633
+ ghostH: drag.ghostH,
8634
+ ghostSrc: drag.ghostSrc,
8635
+ dropIndicatorClientY: indicatorClientY,
8636
+ dropIndex,
8637
+ imagePath: drag.imagePath
8638
+ });
8639
+ };
8640
+ const onMouseUp = (e) => {
8641
+ const drag = imageDragRef.current;
8642
+ if (!drag) return;
8643
+ imageDragRef.current = null;
8644
+ if (!drag.active) {
8645
+ setImageDragState(null);
8646
+ return;
8647
+ }
8648
+ const container = containerRef.current;
8649
+ const { dropIndex } = getImageDropTarget(container, e.clientY, drag.imagePath);
8650
+ setImageDragState(null);
8651
+ moveImageInDoc(drag.imagePath, dropIndex, engine);
8652
+ };
8653
+ document.addEventListener("mousemove", onMouseMove);
8654
+ document.addEventListener("mouseup", onMouseUp);
8655
+ return () => {
8656
+ document.removeEventListener("mousemove", onMouseMove);
8657
+ document.removeEventListener("mouseup", onMouseUp);
8658
+ };
8659
+ }, [engine]);
8604
8660
  react.useEffect(() => {
8605
8661
  const container = containerRef.current;
8606
8662
  if (!container) return;
@@ -8718,6 +8774,19 @@ function EditorCore({
8718
8774
  ].join(" ")
8719
8775
  }
8720
8776
  ),
8777
+ imageDragState && (() => {
8778
+ const container = containerRef.current;
8779
+ if (!container) return null;
8780
+ const cr = container.getBoundingClientRect();
8781
+ const relY = imageDragState.dropIndicatorClientY - cr.top + container.scrollTop;
8782
+ return /* @__PURE__ */ jsxRuntime.jsx(
8783
+ "div",
8784
+ {
8785
+ className: "editor-image-drop-indicator",
8786
+ style: { top: relY }
8787
+ }
8788
+ );
8789
+ })(),
8721
8790
  linkTooltip && /* @__PURE__ */ jsxRuntime.jsxs(
8722
8791
  "div",
8723
8792
  {
@@ -8778,6 +8847,26 @@ function EditorCore({
8778
8847
  onClose: () => setSelectedImagePath(null)
8779
8848
  }
8780
8849
  ),
8850
+ imageDragState && /* @__PURE__ */ jsxRuntime.jsx(
8851
+ "div",
8852
+ {
8853
+ className: "editor-image-drag-ghost",
8854
+ style: {
8855
+ left: imageDragState.ghostX,
8856
+ top: imageDragState.ghostY,
8857
+ width: imageDragState.ghostW,
8858
+ height: imageDragState.ghostH
8859
+ },
8860
+ children: /* @__PURE__ */ jsxRuntime.jsx(
8861
+ "img",
8862
+ {
8863
+ src: imageDragState.ghostSrc,
8864
+ alt: "",
8865
+ style: { width: "100%", height: "100%", objectFit: "contain", display: "block" }
8866
+ }
8867
+ )
8868
+ }
8869
+ ),
8781
8870
  !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
8782
8871
  "div",
8783
8872
  {
@@ -8800,6 +8889,51 @@ function EditorCore({
8800
8889
  }
8801
8890
  );
8802
8891
  }
8892
+ function getImageDropTarget(container, clientY, imagePath) {
8893
+ const fallback = { dropIndex: imagePath[0], indicatorClientY: clientY };
8894
+ if (!container) return fallback;
8895
+ const blocks = Array.from(
8896
+ container.querySelectorAll(":scope > [data-block-path]")
8897
+ );
8898
+ if (blocks.length === 0) return fallback;
8899
+ const gaps = [];
8900
+ gaps.push({ y: blocks[0].getBoundingClientRect().top, index: 0 });
8901
+ for (let i = 0; i < blocks.length - 1; i++) {
8902
+ const bottom = blocks[i].getBoundingClientRect().bottom;
8903
+ const top = blocks[i + 1].getBoundingClientRect().top;
8904
+ gaps.push({ y: (bottom + top) / 2, index: i + 1 });
8905
+ }
8906
+ gaps.push({ y: blocks[blocks.length - 1].getBoundingClientRect().bottom, index: blocks.length });
8907
+ let best = gaps[0];
8908
+ let bestDist = Math.abs(clientY - best.y);
8909
+ for (const gap of gaps) {
8910
+ const dist = Math.abs(clientY - gap.y);
8911
+ if (dist < bestDist) {
8912
+ bestDist = dist;
8913
+ best = gap;
8914
+ }
8915
+ }
8916
+ return { dropIndex: best.index, indicatorClientY: best.y };
8917
+ }
8918
+ function moveImageInDoc(imagePath, targetIndex, engine) {
8919
+ const state = engine.getState();
8920
+ const imageNode = getNodeAtPath(state.doc, imagePath);
8921
+ if (!imageNode) return;
8922
+ const isTopLevel = imagePath.length === 1;
8923
+ const topLevelIndex = imagePath[0];
8924
+ let adj = targetIndex;
8925
+ if (isTopLevel) {
8926
+ if (targetIndex > topLevelIndex) adj = targetIndex - 1;
8927
+ adj = Math.max(0, Math.min(state.doc.children.length - 1, adj));
8928
+ if (adj === topLevelIndex) return;
8929
+ } else {
8930
+ adj = Math.max(0, Math.min(state.doc.children.length, adj));
8931
+ }
8932
+ const tr = createTransaction();
8933
+ tr.steps.push({ type: "delete_node", path: imagePath });
8934
+ tr.steps.push({ type: "insert_node", parentPath: [], index: adj, node: imageNode });
8935
+ engine.dispatch(tr);
8936
+ }
8803
8937
  var BLOCK_TAGS = "p|h[1-6]|ul|ol|li|blockquote|pre|figure|table|thead|tbody|tr|th|td";
8804
8938
  var _OPEN_ONLY_RE = new RegExp(`^<(${BLOCK_TAGS})(?:\\s[^>]*)?>$`, "i");
8805
8939
  var _CLOSE_ONLY_RE = new RegExp(`^<\\/(${BLOCK_TAGS})>$`, "i");