@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.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,21 @@ 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
+ 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
+ }
8520
8535
  return;
8521
8536
  }
8522
8537
  setSelectedImagePath(null);
@@ -8569,6 +8584,7 @@ function EditorCore({
8569
8584
  engine.dispatch(tr);
8570
8585
  }, [engine]);
8571
8586
  const imageResizeRef = react.useRef(null);
8587
+ const imageDragRef = react.useRef(null);
8572
8588
  react.useEffect(() => {
8573
8589
  const onMouseMove = (e) => {
8574
8590
  const drag = imageResizeRef.current;
@@ -8601,6 +8617,48 @@ function EditorCore({
8601
8617
  document.removeEventListener("mouseup", onMouseUp);
8602
8618
  };
8603
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]);
8604
8662
  react.useEffect(() => {
8605
8663
  const container = containerRef.current;
8606
8664
  if (!container) return;
@@ -8718,6 +8776,19 @@ function EditorCore({
8718
8776
  ].join(" ")
8719
8777
  }
8720
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
+ })(),
8721
8792
  linkTooltip && /* @__PURE__ */ jsxRuntime.jsxs(
8722
8793
  "div",
8723
8794
  {
@@ -8778,6 +8849,26 @@ function EditorCore({
8778
8849
  onClose: () => setSelectedImagePath(null)
8779
8850
  }
8780
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
+ ),
8781
8872
  !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
8782
8873
  "div",
8783
8874
  {
@@ -8800,6 +8891,46 @@ function EditorCore({
8800
8891
  }
8801
8892
  );
8802
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
+ }
8803
8934
  var BLOCK_TAGS = "p|h[1-6]|ul|ol|li|blockquote|pre|figure|table|thead|tbody|tr|th|td";
8804
8935
  var _OPEN_ONLY_RE = new RegExp(`^<(${BLOCK_TAGS})(?:\\s[^>]*)?>$`, "i");
8805
8936
  var _CLOSE_ONLY_RE = new RegExp(`^<\\/(${BLOCK_TAGS})>$`, "i");