@trafica/editor 1.0.45 → 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.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,21 @@ 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
+ if (path.length === 1) {
8521
+ const imgEl = fig.querySelector("img");
8522
+ const rect = imgEl == null ? void 0 : imgEl.getBoundingClientRect();
8523
+ imageDragRef.current = {
8524
+ imagePath: path,
8525
+ startX: e.clientX,
8526
+ startY: e.clientY,
8527
+ active: false,
8528
+ ghostW: Math.min((rect == null ? void 0 : rect.width) || 300, 320),
8529
+ ghostH: Math.min((rect == null ? void 0 : rect.height) || 200, 200),
8530
+ ghostSrc: (imgEl == null ? void 0 : imgEl.src) || ""
8531
+ };
8532
+ }
8518
8533
  return;
8519
8534
  }
8520
8535
  setSelectedImagePath(null);
@@ -8567,6 +8582,7 @@ function EditorCore({
8567
8582
  engine.dispatch(tr);
8568
8583
  }, [engine]);
8569
8584
  const imageResizeRef = useRef(null);
8585
+ const imageDragRef = useRef(null);
8570
8586
  useEffect(() => {
8571
8587
  const onMouseMove = (e) => {
8572
8588
  const drag = imageResizeRef.current;
@@ -8599,6 +8615,48 @@ function EditorCore({
8599
8615
  document.removeEventListener("mouseup", onMouseUp);
8600
8616
  };
8601
8617
  }, [engine]);
8618
+ 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]);
8602
8660
  useEffect(() => {
8603
8661
  const container = containerRef.current;
8604
8662
  if (!container) return;
@@ -8716,6 +8774,19 @@ function EditorCore({
8716
8774
  ].join(" ")
8717
8775
  }
8718
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__ */ jsx(
8783
+ "div",
8784
+ {
8785
+ className: "editor-image-drop-indicator",
8786
+ style: { top: relY }
8787
+ }
8788
+ );
8789
+ })(),
8719
8790
  linkTooltip && /* @__PURE__ */ jsxs(
8720
8791
  "div",
8721
8792
  {
@@ -8776,6 +8847,26 @@ function EditorCore({
8776
8847
  onClose: () => setSelectedImagePath(null)
8777
8848
  }
8778
8849
  ),
8850
+ imageDragState && /* @__PURE__ */ 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__ */ jsx(
8861
+ "img",
8862
+ {
8863
+ src: imageDragState.ghostSrc,
8864
+ alt: "",
8865
+ style: { width: "100%", height: "100%", objectFit: "contain", display: "block" }
8866
+ }
8867
+ )
8868
+ }
8869
+ ),
8779
8870
  !readOnly && /* @__PURE__ */ jsx(
8780
8871
  "div",
8781
8872
  {
@@ -8798,6 +8889,46 @@ function EditorCore({
8798
8889
  }
8799
8890
  );
8800
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 oldIndex = imagePath[imagePath.length - 1];
8923
+ const parentPath = imagePath.slice(0, -1);
8924
+ let adj = targetIndex > oldIndex ? targetIndex - 1 : targetIndex;
8925
+ adj = Math.max(0, Math.min(state.doc.children.length - 1, adj));
8926
+ if (adj === oldIndex) return;
8927
+ const tr = createTransaction();
8928
+ tr.steps.push({ type: "delete_node", path: imagePath });
8929
+ tr.steps.push({ type: "insert_node", parentPath, index: adj, node: imageNode });
8930
+ engine.dispatch(tr);
8931
+ }
8801
8932
  var BLOCK_TAGS = "p|h[1-6]|ul|ol|li|blockquote|pre|figure|table|thead|tbody|tr|th|td";
8802
8933
  var _OPEN_ONLY_RE = new RegExp(`^<(${BLOCK_TAGS})(?:\\s[^>]*)?>$`, "i");
8803
8934
  var _CLOSE_ONLY_RE = new RegExp(`^<\\/(${BLOCK_TAGS})>$`, "i");