@trafica/editor 1.0.46 → 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.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);
@@ -8519,19 +8534,19 @@ function EditorCore({
8519
8534
  e.preventDefault();
8520
8535
  const path = JSON.parse(fig.dataset.imagePath);
8521
8536
  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
- }
8537
+ const imgEl = fig.querySelector("img");
8538
+ const figRect = fig.getBoundingClientRect();
8539
+ imageDragRef.current = {
8540
+ imagePath: path,
8541
+ startX: e.clientX,
8542
+ startY: e.clientY,
8543
+ active: false,
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
8549
+ };
8535
8550
  return;
8536
8551
  }
8537
8552
  setSelectedImagePath(null);
@@ -8618,39 +8633,47 @@ function EditorCore({
8618
8633
  };
8619
8634
  }, [engine]);
8620
8635
  react.useEffect(() => {
8621
- const DRAG_THRESHOLD = 6;
8636
+ const DRAG_THRESHOLD = 4;
8622
8637
  const onMouseMove = (e) => {
8623
8638
  const drag = imageDragRef.current;
8624
8639
  if (!drag) return;
8625
8640
  const dx = e.clientX - drag.startX;
8626
8641
  const dy = e.clientY - drag.startY;
8627
8642
  if (!drag.active && Math.sqrt(dx * dx + dy * dy) < DRAG_THRESHOLD) return;
8628
- drag.active = true;
8643
+ if (!drag.active) {
8644
+ drag.active = true;
8645
+ setImageDragging(true);
8646
+ document.body.style.userSelect = "none";
8647
+ }
8629
8648
  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
- });
8649
+ if (!container) return;
8650
+ const cr = container.getBoundingClientRect();
8651
+ const x = Math.max(0, e.clientX - cr.left - drag.offsetX + container.scrollLeft);
8652
+ const y = Math.max(0, e.clientY - cr.top - drag.offsetY + container.scrollTop);
8653
+ const fig = container.querySelector(
8654
+ `[data-image-path="${JSON.stringify(drag.imagePath)}"]`
8655
+ );
8656
+ if (fig) {
8657
+ fig.style.position = "absolute";
8658
+ fig.style.left = `${x}px`;
8659
+ fig.style.top = `${y}px`;
8660
+ fig.style.margin = "0";
8661
+ fig.style.zIndex = "10";
8662
+ }
8641
8663
  };
8642
8664
  const onMouseUp = (e) => {
8643
8665
  const drag = imageDragRef.current;
8644
8666
  if (!drag) return;
8645
8667
  imageDragRef.current = null;
8646
- if (!drag.active) {
8647
- setImageDragState(null);
8648
- return;
8649
- }
8668
+ document.body.style.userSelect = "";
8669
+ setImageDragging(false);
8670
+ if (!drag.active) return;
8650
8671
  const container = containerRef.current;
8651
- const { dropIndex } = getImageDropTarget(container, e.clientY, drag.imagePath);
8652
- setImageDragState(null);
8653
- moveImageInDoc(drag.imagePath, dropIndex, engine);
8672
+ if (!container) return;
8673
+ const cr = container.getBoundingClientRect();
8674
+ const x = Math.max(0, e.clientX - cr.left - drag.offsetX + container.scrollLeft);
8675
+ const y = Math.max(0, e.clientY - cr.top - drag.offsetY + container.scrollTop);
8676
+ setImageAttr(drag.imagePath, { x: Math.round(x), y: Math.round(y) })(engine);
8654
8677
  };
8655
8678
  document.addEventListener("mousemove", onMouseMove);
8656
8679
  document.addEventListener("mouseup", onMouseUp);
@@ -8776,19 +8799,6 @@ function EditorCore({
8776
8799
  ].join(" ")
8777
8800
  }
8778
8801
  ),
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
- })(),
8792
8802
  linkTooltip && /* @__PURE__ */ jsxRuntime.jsxs(
8793
8803
  "div",
8794
8804
  {
@@ -8849,26 +8859,6 @@ function EditorCore({
8849
8859
  onClose: () => setSelectedImagePath(null)
8850
8860
  }
8851
8861
  ),
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
- ),
8872
8862
  !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
8873
8863
  "div",
8874
8864
  {
@@ -8891,46 +8881,6 @@ function EditorCore({
8891
8881
  }
8892
8882
  );
8893
8883
  }
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
- }
8934
8884
  var BLOCK_TAGS = "p|h[1-6]|ul|ol|li|blockquote|pre|figure|table|thead|tbody|tr|th|td";
8935
8885
  var _OPEN_ONLY_RE = new RegExp(`^<(${BLOCK_TAGS})(?:\\s[^>]*)?>$`, "i");
8936
8886
  var _CLOSE_ONLY_RE = new RegExp(`^<\\/(${BLOCK_TAGS})>$`, "i");