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