@trafica/editor 1.0.47 → 1.0.48

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
@@ -1036,26 +1036,33 @@ function renderCodeBlock(node, path) {
1036
1036
  return wrapper;
1037
1037
  }
1038
1038
  function renderImage(node, path) {
1039
- var _a, _b, _c, _d, _e, _f, _g;
1039
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1040
1040
  const wrapper = document.createElement("figure");
1041
1041
  wrapper.dataset.blockPath = JSON.stringify(path);
1042
1042
  wrapper.dataset.imagePath = JSON.stringify(path);
1043
1043
  wrapper.contentEditable = "false";
1044
1044
  const align = (_a = node.attrs) == null ? void 0 : _a.align;
1045
+ const fx = (_b = node.attrs) == null ? void 0 : _b.x;
1046
+ const fy = (_c = node.attrs) == null ? void 0 : _c.y;
1045
1047
  wrapper.className = [
1046
1048
  "editor-image-wrapper",
1047
- align === "center" ? "editor-image-center" : "",
1048
- align === "right" ? "editor-image-right" : "",
1049
- align === "left" ? "editor-image-left" : ""
1049
+ fx !== void 0 ? "editor-image-floating" : "",
1050
+ !fx && align === "center" ? "editor-image-center" : "",
1051
+ !fx && align === "right" ? "editor-image-right" : "",
1052
+ !fx && align === "left" ? "editor-image-left" : ""
1050
1053
  ].filter(Boolean).join(" ");
1054
+ if (fx !== void 0) {
1055
+ wrapper.style.left = `${fx}px`;
1056
+ wrapper.style.top = `${fy != null ? fy : 0}px`;
1057
+ }
1051
1058
  const img = document.createElement("img");
1052
- img.src = (_c = (_b = node.attrs) == null ? void 0 : _b.src) != null ? _c : "";
1053
- img.alt = (_e = (_d = node.attrs) == null ? void 0 : _d.alt) != null ? _e : "";
1059
+ img.src = (_e = (_d = node.attrs) == null ? void 0 : _d.src) != null ? _e : "";
1060
+ img.alt = (_g = (_f = node.attrs) == null ? void 0 : _f.alt) != null ? _g : "";
1054
1061
  img.className = "editor-image";
1055
1062
  img.draggable = false;
1056
- if ((_f = node.attrs) == null ? void 0 : _f.width) img.style.width = `${node.attrs.width}px`;
1063
+ if ((_h = node.attrs) == null ? void 0 : _h.width) img.style.width = `${node.attrs.width}px`;
1057
1064
  wrapper.appendChild(img);
1058
- const caption = (_g = node.attrs) == null ? void 0 : _g.caption;
1065
+ const caption = (_i = node.attrs) == null ? void 0 : _i.caption;
1059
1066
  if (caption !== void 0) {
1060
1067
  const figcaption = document.createElement("figcaption");
1061
1068
  figcaption.className = "editor-image-caption";
@@ -1642,7 +1649,7 @@ var htmlSerializer = {
1642
1649
  }
1643
1650
  };
1644
1651
  function serializeBlock(node, idCounts = /* @__PURE__ */ new Map(), inCell = false) {
1645
- 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;
1652
+ 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;
1646
1653
  switch (node.type) {
1647
1654
  case "paragraph": {
1648
1655
  const alignVal = (_a = node.attrs) == null ? void 0 : _a.align;
@@ -1694,16 +1701,19 @@ function serializeBlock(node, idCounts = /* @__PURE__ */ new Map(), inCell = fal
1694
1701
  const width = ((_m = node.attrs) == null ? void 0 : _m.width) ? ` width="${node.attrs.width}"` : "";
1695
1702
  const align = ((_n = node.attrs) == null ? void 0 : _n.align) ? ` data-align="${node.attrs.align}"` : "";
1696
1703
  const caption = ((_o = node.attrs) == null ? void 0 : _o.caption) ? escapeHTML(node.attrs.caption) : "";
1704
+ const x = (_p = node.attrs) == null ? void 0 : _p.x;
1705
+ const y = (_q = node.attrs) == null ? void 0 : _q.y;
1706
+ 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;"` : "";
1697
1707
  if (caption) {
1698
- return `<figure${align}><img src="${src}" alt="${alt}"${width} /><figcaption>${caption}</figcaption></figure>`;
1708
+ return `<figure${align}${posAttrs}><img src="${src}" alt="${alt}"${width} /><figcaption>${caption}</figcaption></figure>`;
1699
1709
  }
1700
- return `<figure${align}><img src="${src}" alt="${alt}"${width} /></figure>`;
1710
+ return `<figure${align}${posAttrs}><img src="${src}" alt="${alt}"${width} /></figure>`;
1701
1711
  }
1702
1712
  case "horizontal_rule":
1703
1713
  return "<hr />";
1704
1714
  case "table": {
1705
1715
  const rows = node.children.map((c) => serializeBlock(c)).join("");
1706
- const colWidths = (_q = (_p = node.attrs) == null ? void 0 : _p.colWidths) != null ? _q : [];
1716
+ const colWidths = (_s = (_r = node.attrs) == null ? void 0 : _r.colWidths) != null ? _s : [];
1707
1717
  let colgroup = "";
1708
1718
  if (colWidths.length > 0) {
1709
1719
  const total = colWidths.reduce((s, w) => s + w, 0) || colWidths.length * 120;
@@ -1716,15 +1726,15 @@ function serializeBlock(node, idCounts = /* @__PURE__ */ new Map(), inCell = fal
1716
1726
  return `<tr>${cells}</tr>`;
1717
1727
  }
1718
1728
  case "table_cell": {
1719
- if ((_r = node.attrs) == null ? void 0 : _r.covered) return "";
1720
- const cs = ((_s = node.attrs) == null ? void 0 : _s.colspan) > 1 ? ` colspan="${(_t = node.attrs) == null ? void 0 : _t.colspan}"` : "";
1721
- const rs = ((_u = node.attrs) == null ? void 0 : _u.rowspan) > 1 ? ` rowspan="${(_v = node.attrs) == null ? void 0 : _v.rowspan}"` : "";
1729
+ if ((_t = node.attrs) == null ? void 0 : _t.covered) return "";
1730
+ const cs = ((_u = node.attrs) == null ? void 0 : _u.colspan) > 1 ? ` colspan="${(_v = node.attrs) == null ? void 0 : _v.colspan}"` : "";
1731
+ const rs = ((_w = node.attrs) == null ? void 0 : _w.rowspan) > 1 ? ` rowspan="${(_x = node.attrs) == null ? void 0 : _x.rowspan}"` : "";
1722
1732
  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>`;
1723
1733
  }
1724
1734
  case "table_header": {
1725
- if ((_w = node.attrs) == null ? void 0 : _w.covered) return "";
1726
- const cs = ((_x = node.attrs) == null ? void 0 : _x.colspan) > 1 ? ` colspan="${(_y = node.attrs) == null ? void 0 : _y.colspan}"` : "";
1727
- const rs = ((_z = node.attrs) == null ? void 0 : _z.rowspan) > 1 ? ` rowspan="${(_A = node.attrs) == null ? void 0 : _A.rowspan}"` : "";
1735
+ if ((_y = node.attrs) == null ? void 0 : _y.covered) return "";
1736
+ const cs = ((_z = node.attrs) == null ? void 0 : _z.colspan) > 1 ? ` colspan="${(_A = node.attrs) == null ? void 0 : _A.colspan}"` : "";
1737
+ const rs = ((_B = node.attrs) == null ? void 0 : _B.rowspan) > 1 ? ` rowspan="${(_C = node.attrs) == null ? void 0 : _C.rowspan}"` : "";
1728
1738
  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>`;
1729
1739
  }
1730
1740
  default:
@@ -1878,6 +1888,10 @@ function parseHTMLNode(node) {
1878
1888
  const img = el.querySelector("img");
1879
1889
  const caption = (_h = (_g = el.querySelector("figcaption")) == null ? void 0 : _g.textContent) != null ? _h : "";
1880
1890
  const align = (_i = el.getAttribute("data-align")) != null ? _i : "";
1891
+ const dxRaw = el.getAttribute("data-x");
1892
+ const dyRaw = el.getAttribute("data-y");
1893
+ const fx = dxRaw !== null ? parseFloat(dxRaw) : void 0;
1894
+ const fy = dyRaw !== null ? parseFloat(dyRaw) : void 0;
1881
1895
  if (img) {
1882
1896
  return {
1883
1897
  type: "image",
@@ -1886,7 +1900,8 @@ function parseHTMLNode(node) {
1886
1900
  alt: (_k = img.getAttribute("alt")) != null ? _k : "",
1887
1901
  ...img.getAttribute("width") ? { width: parseFloat(img.getAttribute("width")) } : {},
1888
1902
  ...align ? { align } : {},
1889
- ...caption ? { caption } : {}
1903
+ ...caption ? { caption } : {},
1904
+ ...fx !== void 0 && !isNaN(fx) ? { x: fx, y: fy != null ? fy : 0 } : {}
1890
1905
  },
1891
1906
  children: []
1892
1907
  };
@@ -8034,7 +8049,7 @@ function EditorCore({
8034
8049
  const isComposingRef = useRef(false);
8035
8050
  const stateRef = useRef(engine.getState());
8036
8051
  const [selectedImagePath, setSelectedImagePath] = useState(null);
8037
- const [imageDragState, setImageDragState] = useState(null);
8052
+ const [imageDragging, setImageDragging] = useState(false);
8038
8053
  const [findReplaceOpen, setFindReplaceOpen] = useState(false);
8039
8054
  const [findReplaceMode, setFindReplaceMode] = useState("find");
8040
8055
  const [linkPopupOpen, setLinkPopupOpen] = useState(false);
@@ -8518,15 +8533,17 @@ function EditorCore({
8518
8533
  const path = JSON.parse(fig.dataset.imagePath);
8519
8534
  setSelectedImagePath(path);
8520
8535
  const imgEl = fig.querySelector("img");
8521
- const rect = imgEl == null ? void 0 : imgEl.getBoundingClientRect();
8536
+ const figRect = fig.getBoundingClientRect();
8522
8537
  imageDragRef.current = {
8523
8538
  imagePath: path,
8524
8539
  startX: e.clientX,
8525
8540
  startY: e.clientY,
8526
8541
  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) || ""
8542
+ ghostW: Math.min(figRect.width || 300, 320),
8543
+ ghostH: Math.min(figRect.height || 200, 200),
8544
+ ghostSrc: (imgEl == null ? void 0 : imgEl.src) || "",
8545
+ offsetX: e.clientX - figRect.left,
8546
+ offsetY: e.clientY - figRect.top
8530
8547
  };
8531
8548
  return;
8532
8549
  }
@@ -8614,39 +8631,47 @@ function EditorCore({
8614
8631
  };
8615
8632
  }, [engine]);
8616
8633
  useEffect(() => {
8617
- const DRAG_THRESHOLD = 6;
8634
+ const DRAG_THRESHOLD = 4;
8618
8635
  const onMouseMove = (e) => {
8619
8636
  const drag = imageDragRef.current;
8620
8637
  if (!drag) return;
8621
8638
  const dx = e.clientX - drag.startX;
8622
8639
  const dy = e.clientY - drag.startY;
8623
8640
  if (!drag.active && Math.sqrt(dx * dx + dy * dy) < DRAG_THRESHOLD) return;
8624
- drag.active = true;
8641
+ if (!drag.active) {
8642
+ drag.active = true;
8643
+ setImageDragging(true);
8644
+ document.body.style.userSelect = "none";
8645
+ }
8625
8646
  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
- });
8647
+ if (!container) return;
8648
+ const cr = container.getBoundingClientRect();
8649
+ const x = Math.max(0, e.clientX - cr.left - drag.offsetX + container.scrollLeft);
8650
+ const y = Math.max(0, e.clientY - cr.top - drag.offsetY + container.scrollTop);
8651
+ const fig = container.querySelector(
8652
+ `[data-image-path="${JSON.stringify(drag.imagePath)}"]`
8653
+ );
8654
+ if (fig) {
8655
+ fig.style.position = "absolute";
8656
+ fig.style.left = `${x}px`;
8657
+ fig.style.top = `${y}px`;
8658
+ fig.style.margin = "0";
8659
+ fig.style.zIndex = "10";
8660
+ }
8637
8661
  };
8638
8662
  const onMouseUp = (e) => {
8639
8663
  const drag = imageDragRef.current;
8640
8664
  if (!drag) return;
8641
8665
  imageDragRef.current = null;
8642
- if (!drag.active) {
8643
- setImageDragState(null);
8644
- return;
8645
- }
8666
+ document.body.style.userSelect = "";
8667
+ setImageDragging(false);
8668
+ if (!drag.active) return;
8646
8669
  const container = containerRef.current;
8647
- const { dropIndex } = getImageDropTarget(container, e.clientY, drag.imagePath);
8648
- setImageDragState(null);
8649
- moveImageInDoc(drag.imagePath, dropIndex, engine);
8670
+ if (!container) return;
8671
+ const cr = container.getBoundingClientRect();
8672
+ const x = Math.max(0, e.clientX - cr.left - drag.offsetX + container.scrollLeft);
8673
+ const y = Math.max(0, e.clientY - cr.top - drag.offsetY + container.scrollTop);
8674
+ setImageAttr(drag.imagePath, { x: Math.round(x), y: Math.round(y) })(engine);
8650
8675
  };
8651
8676
  document.addEventListener("mousemove", onMouseMove);
8652
8677
  document.addEventListener("mouseup", onMouseUp);
@@ -8772,19 +8797,6 @@ function EditorCore({
8772
8797
  ].join(" ")
8773
8798
  }
8774
8799
  ),
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
- })(),
8788
8800
  linkTooltip && /* @__PURE__ */ jsxs(
8789
8801
  "div",
8790
8802
  {
@@ -8845,26 +8857,6 @@ function EditorCore({
8845
8857
  onClose: () => setSelectedImagePath(null)
8846
8858
  }
8847
8859
  ),
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
- ),
8868
8860
  !readOnly && /* @__PURE__ */ jsx(
8869
8861
  "div",
8870
8862
  {
@@ -8887,51 +8879,6 @@ function EditorCore({
8887
8879
  }
8888
8880
  );
8889
8881
  }
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
- }
8935
8882
  var BLOCK_TAGS = "p|h[1-6]|ul|ol|li|blockquote|pre|figure|table|thead|tbody|tr|th|td";
8936
8883
  var _OPEN_ONLY_RE = new RegExp(`^<(${BLOCK_TAGS})(?:\\s[^>]*)?>$`, "i");
8937
8884
  var _CLOSE_ONLY_RE = new RegExp(`^<\\/(${BLOCK_TAGS})>$`, "i");