@trafica/editor 1.0.47 → 1.0.49

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
@@ -1038,26 +1038,33 @@ function renderCodeBlock(node, path) {
1038
1038
  return wrapper;
1039
1039
  }
1040
1040
  function renderImage(node, path) {
1041
- var _a, _b, _c, _d, _e, _f, _g;
1041
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1042
1042
  const wrapper = document.createElement("figure");
1043
1043
  wrapper.dataset.blockPath = JSON.stringify(path);
1044
1044
  wrapper.dataset.imagePath = JSON.stringify(path);
1045
1045
  wrapper.contentEditable = "false";
1046
1046
  const align = (_a = node.attrs) == null ? void 0 : _a.align;
1047
+ const fx = (_b = node.attrs) == null ? void 0 : _b.x;
1048
+ const fy = (_c = node.attrs) == null ? void 0 : _c.y;
1047
1049
  wrapper.className = [
1048
1050
  "editor-image-wrapper",
1049
- align === "center" ? "editor-image-center" : "",
1050
- align === "right" ? "editor-image-right" : "",
1051
- align === "left" ? "editor-image-left" : ""
1051
+ fx !== void 0 ? "editor-image-floating" : "",
1052
+ !fx && align === "center" ? "editor-image-center" : "",
1053
+ !fx && align === "right" ? "editor-image-right" : "",
1054
+ !fx && align === "left" ? "editor-image-left" : ""
1052
1055
  ].filter(Boolean).join(" ");
1056
+ if (fx !== void 0) {
1057
+ wrapper.style.left = `${fx}px`;
1058
+ wrapper.style.top = `${fy != null ? fy : 0}px`;
1059
+ }
1053
1060
  const img = document.createElement("img");
1054
- img.src = (_c = (_b = node.attrs) == null ? void 0 : _b.src) != null ? _c : "";
1055
- img.alt = (_e = (_d = node.attrs) == null ? void 0 : _d.alt) != null ? _e : "";
1061
+ img.src = (_e = (_d = node.attrs) == null ? void 0 : _d.src) != null ? _e : "";
1062
+ img.alt = (_g = (_f = node.attrs) == null ? void 0 : _f.alt) != null ? _g : "";
1056
1063
  img.className = "editor-image";
1057
1064
  img.draggable = false;
1058
- if ((_f = node.attrs) == null ? void 0 : _f.width) img.style.width = `${node.attrs.width}px`;
1065
+ if ((_h = node.attrs) == null ? void 0 : _h.width) img.style.width = `${node.attrs.width}px`;
1059
1066
  wrapper.appendChild(img);
1060
- const caption = (_g = node.attrs) == null ? void 0 : _g.caption;
1067
+ const caption = (_i = node.attrs) == null ? void 0 : _i.caption;
1061
1068
  if (caption !== void 0) {
1062
1069
  const figcaption = document.createElement("figcaption");
1063
1070
  figcaption.className = "editor-image-caption";
@@ -1644,7 +1651,7 @@ var htmlSerializer = {
1644
1651
  }
1645
1652
  };
1646
1653
  function serializeBlock(node, idCounts = /* @__PURE__ */ new Map(), inCell = false) {
1647
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A;
1654
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C;
1648
1655
  switch (node.type) {
1649
1656
  case "paragraph": {
1650
1657
  const alignVal = (_a = node.attrs) == null ? void 0 : _a.align;
@@ -1696,16 +1703,19 @@ function serializeBlock(node, idCounts = /* @__PURE__ */ new Map(), inCell = fal
1696
1703
  const width = ((_m = node.attrs) == null ? void 0 : _m.width) ? ` width="${node.attrs.width}"` : "";
1697
1704
  const align = ((_n = node.attrs) == null ? void 0 : _n.align) ? ` data-align="${node.attrs.align}"` : "";
1698
1705
  const caption = ((_o = node.attrs) == null ? void 0 : _o.caption) ? escapeHTML(node.attrs.caption) : "";
1706
+ const x = (_p = node.attrs) == null ? void 0 : _p.x;
1707
+ const y = (_q = node.attrs) == null ? void 0 : _q.y;
1708
+ const posAttrs = x !== void 0 ? ` data-x="${Math.round(x)}" data-y="${Math.round(y != null ? y : 0)}" style="position:absolute;left:${Math.round(x)}px;top:${Math.round(y != null ? y : 0)}px;"` : "";
1699
1709
  if (caption) {
1700
- return `<figure${align}><img src="${src}" alt="${alt}"${width} /><figcaption>${caption}</figcaption></figure>`;
1710
+ return `<figure${align}${posAttrs}><img src="${src}" alt="${alt}"${width} /><figcaption>${caption}</figcaption></figure>`;
1701
1711
  }
1702
- return `<figure${align}><img src="${src}" alt="${alt}"${width} /></figure>`;
1712
+ return `<figure${align}${posAttrs}><img src="${src}" alt="${alt}"${width} /></figure>`;
1703
1713
  }
1704
1714
  case "horizontal_rule":
1705
1715
  return "<hr />";
1706
1716
  case "table": {
1707
1717
  const rows = node.children.map((c) => serializeBlock(c)).join("");
1708
- const colWidths = (_q = (_p = node.attrs) == null ? void 0 : _p.colWidths) != null ? _q : [];
1718
+ const colWidths = (_s = (_r = node.attrs) == null ? void 0 : _r.colWidths) != null ? _s : [];
1709
1719
  let colgroup = "";
1710
1720
  if (colWidths.length > 0) {
1711
1721
  const total = colWidths.reduce((s, w) => s + w, 0) || colWidths.length * 120;
@@ -1718,15 +1728,15 @@ function serializeBlock(node, idCounts = /* @__PURE__ */ new Map(), inCell = fal
1718
1728
  return `<tr>${cells}</tr>`;
1719
1729
  }
1720
1730
  case "table_cell": {
1721
- if ((_r = node.attrs) == null ? void 0 : _r.covered) return "";
1722
- const cs = ((_s = node.attrs) == null ? void 0 : _s.colspan) > 1 ? ` colspan="${(_t = node.attrs) == null ? void 0 : _t.colspan}"` : "";
1723
- const rs = ((_u = node.attrs) == null ? void 0 : _u.rowspan) > 1 ? ` rowspan="${(_v = node.attrs) == null ? void 0 : _v.rowspan}"` : "";
1731
+ if ((_t = node.attrs) == null ? void 0 : _t.covered) return "";
1732
+ const cs = ((_u = node.attrs) == null ? void 0 : _u.colspan) > 1 ? ` colspan="${(_v = node.attrs) == null ? void 0 : _v.colspan}"` : "";
1733
+ const rs = ((_w = node.attrs) == null ? void 0 : _w.rowspan) > 1 ? ` rowspan="${(_x = node.attrs) == null ? void 0 : _x.rowspan}"` : "";
1724
1734
  return `<td${cs}${rs} style="border:1px solid #d1d5db;padding:0.5em 0.75em;vertical-align:top;min-width:40px;word-break:break-word;">${node.children.map((c) => serializeBlock(c, /* @__PURE__ */ new Map(), true)).join("")}</td>`;
1725
1735
  }
1726
1736
  case "table_header": {
1727
- if ((_w = node.attrs) == null ? void 0 : _w.covered) return "";
1728
- const cs = ((_x = node.attrs) == null ? void 0 : _x.colspan) > 1 ? ` colspan="${(_y = node.attrs) == null ? void 0 : _y.colspan}"` : "";
1729
- const rs = ((_z = node.attrs) == null ? void 0 : _z.rowspan) > 1 ? ` rowspan="${(_A = node.attrs) == null ? void 0 : _A.rowspan}"` : "";
1737
+ if ((_y = node.attrs) == null ? void 0 : _y.covered) return "";
1738
+ const cs = ((_z = node.attrs) == null ? void 0 : _z.colspan) > 1 ? ` colspan="${(_A = node.attrs) == null ? void 0 : _A.colspan}"` : "";
1739
+ const rs = ((_B = node.attrs) == null ? void 0 : _B.rowspan) > 1 ? ` rowspan="${(_C = node.attrs) == null ? void 0 : _C.rowspan}"` : "";
1730
1740
  return `<th${cs}${rs} style="border:1px solid #d1d5db;padding:0.5em 0.75em;vertical-align:top;min-width:40px;word-break:break-word;background:#f9fafb;font-weight:600;text-align:left;">${node.children.map((c) => serializeBlock(c, /* @__PURE__ */ new Map(), true)).join("")}</th>`;
1731
1741
  }
1732
1742
  default:
@@ -1880,6 +1890,10 @@ function parseHTMLNode(node) {
1880
1890
  const img = el.querySelector("img");
1881
1891
  const caption = (_h = (_g = el.querySelector("figcaption")) == null ? void 0 : _g.textContent) != null ? _h : "";
1882
1892
  const align = (_i = el.getAttribute("data-align")) != null ? _i : "";
1893
+ const dxRaw = el.getAttribute("data-x");
1894
+ const dyRaw = el.getAttribute("data-y");
1895
+ const fx = dxRaw !== null ? parseFloat(dxRaw) : void 0;
1896
+ const fy = dyRaw !== null ? parseFloat(dyRaw) : void 0;
1883
1897
  if (img) {
1884
1898
  return {
1885
1899
  type: "image",
@@ -1888,7 +1902,8 @@ function parseHTMLNode(node) {
1888
1902
  alt: (_k = img.getAttribute("alt")) != null ? _k : "",
1889
1903
  ...img.getAttribute("width") ? { width: parseFloat(img.getAttribute("width")) } : {},
1890
1904
  ...align ? { align } : {},
1891
- ...caption ? { caption } : {}
1905
+ ...caption ? { caption } : {},
1906
+ ...fx !== void 0 && !isNaN(fx) ? { x: fx, y: fy != null ? fy : 0 } : {}
1892
1907
  },
1893
1908
  children: []
1894
1909
  };
@@ -8036,7 +8051,7 @@ function EditorCore({
8036
8051
  const isComposingRef = react.useRef(false);
8037
8052
  const stateRef = react.useRef(engine.getState());
8038
8053
  const [selectedImagePath, setSelectedImagePath] = react.useState(null);
8039
- const [imageDragState, setImageDragState] = react.useState(null);
8054
+ const [imageDragging, setImageDragging] = react.useState(false);
8040
8055
  const [findReplaceOpen, setFindReplaceOpen] = react.useState(false);
8041
8056
  const [findReplaceMode, setFindReplaceMode] = react.useState("find");
8042
8057
  const [linkPopupOpen, setLinkPopupOpen] = react.useState(false);
@@ -8520,15 +8535,17 @@ function EditorCore({
8520
8535
  const path = JSON.parse(fig.dataset.imagePath);
8521
8536
  setSelectedImagePath(path);
8522
8537
  const imgEl = fig.querySelector("img");
8523
- const rect = imgEl == null ? void 0 : imgEl.getBoundingClientRect();
8538
+ const figRect = fig.getBoundingClientRect();
8524
8539
  imageDragRef.current = {
8525
8540
  imagePath: path,
8526
8541
  startX: e.clientX,
8527
8542
  startY: e.clientY,
8528
8543
  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) || ""
8544
+ ghostW: Math.min(figRect.width || 300, 320),
8545
+ ghostH: Math.min(figRect.height || 200, 200),
8546
+ ghostSrc: (imgEl == null ? void 0 : imgEl.src) || "",
8547
+ offsetX: e.clientX - figRect.left,
8548
+ offsetY: e.clientY - figRect.top
8532
8549
  };
8533
8550
  return;
8534
8551
  }
@@ -8616,39 +8633,58 @@ function EditorCore({
8616
8633
  };
8617
8634
  }, [engine]);
8618
8635
  react.useEffect(() => {
8619
- const DRAG_THRESHOLD = 6;
8636
+ const DRAG_THRESHOLD = 4;
8620
8637
  const onMouseMove = (e) => {
8621
8638
  const drag = imageDragRef.current;
8622
8639
  if (!drag) return;
8623
8640
  const dx = e.clientX - drag.startX;
8624
8641
  const dy = e.clientY - drag.startY;
8625
8642
  if (!drag.active && Math.sqrt(dx * dx + dy * dy) < DRAG_THRESHOLD) return;
8626
- drag.active = true;
8643
+ if (!drag.active) {
8644
+ drag.active = true;
8645
+ setImageDragging(true);
8646
+ document.body.style.userSelect = "none";
8647
+ }
8627
8648
  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
- });
8649
+ if (!container) return;
8650
+ const cr = container.getBoundingClientRect();
8651
+ const fig = container.querySelector(
8652
+ `[data-image-path="${JSON.stringify(drag.imagePath)}"]`
8653
+ );
8654
+ const figW = (fig == null ? void 0 : fig.offsetWidth) || drag.ghostW;
8655
+ const figH = (fig == null ? void 0 : fig.offsetHeight) || drag.ghostH;
8656
+ const rawX = e.clientX - cr.left - drag.offsetX + container.scrollLeft;
8657
+ const rawY = e.clientY - cr.top - drag.offsetY + container.scrollTop;
8658
+ const x = Math.max(0, Math.min(rawX, container.clientWidth - figW));
8659
+ const y = Math.max(0, Math.min(rawY, container.scrollHeight - figH));
8660
+ if (fig) {
8661
+ fig.style.position = "absolute";
8662
+ fig.style.left = `${x}px`;
8663
+ fig.style.top = `${y}px`;
8664
+ fig.style.margin = "0";
8665
+ fig.style.zIndex = "10";
8666
+ }
8639
8667
  };
8640
8668
  const onMouseUp = (e) => {
8641
8669
  const drag = imageDragRef.current;
8642
8670
  if (!drag) return;
8643
8671
  imageDragRef.current = null;
8644
- if (!drag.active) {
8645
- setImageDragState(null);
8646
- return;
8647
- }
8672
+ document.body.style.userSelect = "";
8673
+ setImageDragging(false);
8674
+ if (!drag.active) return;
8648
8675
  const container = containerRef.current;
8649
- const { dropIndex } = getImageDropTarget(container, e.clientY, drag.imagePath);
8650
- setImageDragState(null);
8651
- moveImageInDoc(drag.imagePath, dropIndex, engine);
8676
+ if (!container) return;
8677
+ const cr = container.getBoundingClientRect();
8678
+ const fig = container.querySelector(
8679
+ `[data-image-path="${JSON.stringify(drag.imagePath)}"]`
8680
+ );
8681
+ const figW = (fig == null ? void 0 : fig.offsetWidth) || drag.ghostW;
8682
+ const figH = (fig == null ? void 0 : fig.offsetHeight) || drag.ghostH;
8683
+ const rawX = e.clientX - cr.left - drag.offsetX + container.scrollLeft;
8684
+ const rawY = e.clientY - cr.top - drag.offsetY + container.scrollTop;
8685
+ const x = Math.max(0, Math.min(rawX, container.clientWidth - figW));
8686
+ const y = Math.max(0, Math.min(rawY, container.scrollHeight - figH));
8687
+ setImageAttr(drag.imagePath, { x: Math.round(x), y: Math.round(y) })(engine);
8652
8688
  };
8653
8689
  document.addEventListener("mousemove", onMouseMove);
8654
8690
  document.addEventListener("mouseup", onMouseUp);
@@ -8774,19 +8810,6 @@ function EditorCore({
8774
8810
  ].join(" ")
8775
8811
  }
8776
8812
  ),
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
- })(),
8790
8813
  linkTooltip && /* @__PURE__ */ jsxRuntime.jsxs(
8791
8814
  "div",
8792
8815
  {
@@ -8847,26 +8870,6 @@ function EditorCore({
8847
8870
  onClose: () => setSelectedImagePath(null)
8848
8871
  }
8849
8872
  ),
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
- ),
8870
8873
  !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
8871
8874
  "div",
8872
8875
  {
@@ -8889,51 +8892,6 @@ function EditorCore({
8889
8892
  }
8890
8893
  );
8891
8894
  }
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
- }
8937
8895
  var BLOCK_TAGS = "p|h[1-6]|ul|ol|li|blockquote|pre|figure|table|thead|tbody|tr|th|td";
8938
8896
  var _OPEN_ONLY_RE = new RegExp(`^<(${BLOCK_TAGS})(?:\\s[^>]*)?>$`, "i");
8939
8897
  var _CLOSE_ONLY_RE = new RegExp(`^<\\/(${BLOCK_TAGS})>$`, "i");