@broberg/cms-inline-edit 0.5.3 → 0.5.5

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.cjs CHANGED
@@ -26,7 +26,10 @@ __export(index_exports, {
26
26
  getConnectedToken: () => getConnectedToken,
27
27
  htmlToMarkdown: () => htmlToMarkdown,
28
28
  initInlineEdit: () => initInlineEdit,
29
- rescanFields: () => rescanFields
29
+ positionPopover: () => positionPopover,
30
+ renderCurrentRefLine: () => renderCurrentRefLine,
31
+ rescanFields: () => rescanFields,
32
+ resolveExistingRef: () => resolveExistingRef
30
33
  });
31
34
  module.exports = __toCommonJS(index_exports);
32
35
 
@@ -102,7 +105,8 @@ var DEFAULT_LABELS = {
102
105
  linkYes: "Ja",
103
106
  linkNo: "Nej",
104
107
  linkEmpty: "Ingen sider fundet",
105
- linkLoading: "Henter sider\u2026"
108
+ linkLoading: "Henter sider\u2026",
109
+ linkCurrent: "Linker til"
106
110
  };
107
111
  var uiLabels = DEFAULT_LABELS;
108
112
  function resolveOptions(options) {
@@ -499,6 +503,22 @@ async function fetchLinkablePages() {
499
503
  linkPages = body.pages ?? [];
500
504
  return linkPages;
501
505
  }
506
+ function positionPopover(el, rect, gap, width) {
507
+ const MARGIN = 8;
508
+ const vh = window.innerHeight;
509
+ const vw = window.innerWidth;
510
+ el.style.maxHeight = `${Math.max(120, vh - MARGIN * 2)}px`;
511
+ el.style.overflowY = "auto";
512
+ el.style.display = "block";
513
+ const h = el.getBoundingClientRect().height || el.offsetHeight || 0;
514
+ let top = rect.bottom + gap;
515
+ if (top + h > vh - MARGIN) {
516
+ const above = rect.top - gap - h;
517
+ top = above >= MARGIN ? above : vh - MARGIN - h;
518
+ }
519
+ el.style.top = `${Math.max(MARGIN, top)}px`;
520
+ el.style.left = `${Math.max(MARGIN, Math.min(rect.left, vw - width - MARGIN))}px`;
521
+ }
502
522
  function toggleLinkDialog(anchor) {
503
523
  if (linkDialog && linkDialog.style.display === "block") {
504
524
  hideLinkDialog();
@@ -510,9 +530,12 @@ function toggleLinkDialog(anchor) {
510
530
  if (!linkDialog) linkDialog = buildLinkDialog();
511
531
  renderLinkDialog();
512
532
  const rect = anchor.getBoundingClientRect();
513
- linkDialog.style.top = `${rect.bottom + 8}px`;
514
- linkDialog.style.left = `${Math.min(Math.max(8, rect.left - 180), window.innerWidth - 400)}px`;
515
- linkDialog.style.display = "block";
533
+ positionPopover(
534
+ linkDialog,
535
+ { top: rect.top, bottom: rect.bottom, left: rect.left - 180 },
536
+ 8,
537
+ 392
538
+ );
516
539
  }
517
540
  function buildLinkDialog() {
518
541
  const d = document.createElement("div");
@@ -534,7 +557,7 @@ function renderLinkDialog() {
534
557
  const editing = !!linkEditing;
535
558
  const existingRef = linkEditing?.getAttribute("data-cms-ref") ?? "";
536
559
  const onPageTab = !editing || !!existingRef;
537
- d.innerHTML = `<div style="display:flex;gap:4px;padding:8px 8px 0"><button type="button" data-testid="inline-link-tab-page" data-tab="page" style="${tabCss(onPageTab)}">${L.linkTabPage}</button><button type="button" data-testid="inline-link-tab-url" data-tab="url" style="${tabCss(!onPageTab)}">${L.linkTabUrl}</button></div><div style="padding:10px 12px 12px"><div data-pane="page" style="display:${onPageTab ? "block" : "none"}"><input data-testid="inline-link-search" data-role="search" placeholder="${L.linkSearch}" style="${fieldCss()}"><div data-role="list" data-testid="inline-link-list" style="margin-top:8px;max-height:196px;overflow-y:auto;border:1px solid #3a3f4a;border-radius:8px;background:#141821"></div></div><div data-pane="url" style="display:${onPageTab ? "none" : "block"}"><label style="${labelCss()}">${L.linkUrl}</label><input data-testid="inline-link-url" data-role="url" placeholder="https://\u2026" style="${fieldCss()}"></div><label style="${labelCss()}">${L.linkText}</label><input data-testid="inline-link-text" data-role="text" placeholder="${L.linkTextAuto}" style="${fieldCss()}"><p data-role="hint" style="font-size:11.5px;color:#9aa3b2;margin:6px 0 0;line-height:1.5"></p><div data-role="live" style="display:flex;gap:8px;margin-top:12px;padding:9px 10px;background:rgba(0,178,255,.08);border:1px solid rgba(0,178,255,.28);border-radius:8px"><p style="margin:0;font-size:11.5px;color:#c9d1dd;line-height:1.55">${L.linkLiveHint}</p></div><div style="display:flex;gap:8px;align-items:center;margin-top:14px"><div data-role="remove"></div><div style="flex:1"></div><button type="button" data-testid="inline-link-cancel" data-role="cancel" style="${btnCss(false)}">${L.linkCancel}</button><button type="button" data-testid="inline-link-submit" data-role="submit" style="${btnCss(true)}" disabled>${editing ? L.linkSave : L.linkInsert}</button></div></div>`;
560
+ d.innerHTML = `<div style="display:flex;gap:4px;padding:8px 8px 0"><button type="button" data-testid="inline-link-tab-page" data-tab="page" style="${tabCss(onPageTab)}">${L.linkTabPage}</button><button type="button" data-testid="inline-link-tab-url" data-tab="url" style="${tabCss(!onPageTab)}">${L.linkTabUrl}</button></div><div style="padding:10px 12px 12px"><div data-pane="page" style="display:${onPageTab ? "block" : "none"}"><input data-testid="inline-link-search" data-role="search" placeholder="${L.linkSearch}" style="${fieldCss()}"><div data-role="current" data-testid="inline-link-current" style="display:none;margin-top:8px;padding:7px 10px;background:rgba(0,178,255,.08);border:1px solid rgba(0,178,255,.28);border-radius:8px;font-size:11.5px;color:#c9d1dd;line-height:1.45"></div><div data-role="list" data-testid="inline-link-list" style="margin-top:8px;max-height:196px;overflow-y:auto;border:1px solid #3a3f4a;border-radius:8px;background:#141821"></div></div><div data-pane="url" style="display:${onPageTab ? "none" : "block"}"><label style="${labelCss()}">${L.linkUrl}</label><input data-testid="inline-link-url" data-role="url" placeholder="https://\u2026" style="${fieldCss()}"></div><label style="${labelCss()}">${L.linkText}</label><input data-testid="inline-link-text" data-role="text" placeholder="${L.linkTextAuto}" style="${fieldCss()}"><p data-role="hint" style="font-size:11.5px;color:#9aa3b2;margin:6px 0 0;line-height:1.5"></p><div data-role="live" style="display:flex;gap:8px;margin-top:12px;padding:9px 10px;background:rgba(0,178,255,.08);border:1px solid rgba(0,178,255,.28);border-radius:8px"><p style="margin:0;font-size:11.5px;color:#c9d1dd;line-height:1.55">${L.linkLiveHint}</p></div><div style="display:flex;gap:8px;align-items:center;margin-top:14px"><div data-role="remove"></div><div style="flex:1"></div><button type="button" data-testid="inline-link-cancel" data-role="cancel" style="${btnCss(false)}">${L.linkCancel}</button><button type="button" data-testid="inline-link-submit" data-role="submit" style="${btnCss(true)}" disabled>${editing ? L.linkSave : L.linkInsert}</button></div></div>`;
538
561
  const q = (role) => d.querySelector(`[data-role="${role}"]`);
539
562
  const text = q("text");
540
563
  const urlIn = q("url");
@@ -589,6 +612,28 @@ function buildRemoveLink() {
589
612
  wrap.appendChild(btn);
590
613
  return wrap;
591
614
  }
615
+ function resolveExistingRef(ref, pages) {
616
+ if (!ref) return null;
617
+ const [collection, ...rest] = ref.split(":");
618
+ const slug = rest.join(":");
619
+ if (!collection || !slug) return null;
620
+ return pages.find((p) => p.collection === collection && p.slug === slug) ?? null;
621
+ }
622
+ function renderCurrentRefLine(box, ref, pages) {
623
+ if (!ref) {
624
+ box.style.display = "none";
625
+ box.innerHTML = "";
626
+ return;
627
+ }
628
+ const label = escapeHtml(uiLabels.linkCurrent);
629
+ const page = resolveExistingRef(ref, pages);
630
+ box.style.display = "block";
631
+ box.innerHTML = page ? `${label}: <strong>${escapeHtml(page.title)}</strong> <span style="color:#9aa3b2;font-family:ui-monospace,Menlo,monospace">${escapeHtml(page.path)}</span>` : `${label}: <span style="font-family:ui-monospace,Menlo,monospace">${escapeHtml(ref)}</span>`;
632
+ }
633
+ function paintCurrentRef(pages) {
634
+ const box = linkDialog?.querySelector('[data-role="current"]');
635
+ if (box) renderCurrentRefLine(box, linkEditing?.getAttribute("data-cms-ref"), pages ?? []);
636
+ }
592
637
  function renderLinkList(filter) {
593
638
  const d = linkDialog;
594
639
  if (!d) return;
@@ -614,19 +659,21 @@ function renderLinkList(filter) {
614
659
  syncLinkDialog();
615
660
  };
616
661
  list.appendChild(row);
662
+ if (on) row.scrollIntoView?.({ block: "nearest" });
617
663
  });
618
664
  };
665
+ const preselect = (pages) => {
666
+ if (!linkPicked) linkPicked = resolveExistingRef(linkEditing?.getAttribute("data-cms-ref"), pages);
667
+ paintCurrentRef(pages);
668
+ };
619
669
  if (linkPages) {
670
+ preselect(linkPages);
620
671
  paint(linkPages);
621
672
  return;
622
673
  }
623
674
  list.innerHTML = `<div style="padding:10px;font-size:12.5px;color:#9aa3b2">${uiLabels.linkLoading}</div>`;
624
675
  fetchLinkablePages().then((pages) => {
625
- const ref = linkEditing?.getAttribute("data-cms-ref");
626
- if (ref && !linkPicked) {
627
- const [c, ...rest] = ref.split(":");
628
- linkPicked = pages.find((p) => p.collection === c && p.slug === rest.join(":")) ?? null;
629
- }
676
+ preselect(pages);
630
677
  paint(pages);
631
678
  syncLinkDialog();
632
679
  }).catch(() => {
@@ -701,9 +748,7 @@ function toggleEmojiPicker(anchor) {
701
748
  const sel = window.getSelection();
702
749
  if (sel && sel.rangeCount > 0) savedRange = sel.getRangeAt(0).cloneRange();
703
750
  const rect = anchor.getBoundingClientRect();
704
- emojiPicker.style.top = `${rect.bottom + 6}px`;
705
- emojiPicker.style.left = `${Math.min(rect.left, window.innerWidth - 240)}px`;
706
- emojiPicker.style.display = "block";
751
+ positionPopover(emojiPicker, rect, 6, 230);
707
752
  }
708
753
  function hideEmojiPicker() {
709
754
  if (emojiPicker) emojiPicker.style.display = "none";
@@ -836,7 +881,7 @@ function serializeBlockChildren(parent) {
836
881
  const s = serializeBlock(node).trim();
837
882
  if (s) blocks.push(s);
838
883
  } else {
839
- inlineRun += serializeInlineNode(node);
884
+ inlineRun = joinInline(inlineRun, serializeInlineNode(node));
840
885
  }
841
886
  });
842
887
  flushInline();
@@ -916,10 +961,41 @@ function serializeList(listEl, ordered) {
916
961
  function escapeAttr(value) {
917
962
  return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;");
918
963
  }
964
+ function preservedLinkAttrs(el) {
965
+ const out = [];
966
+ for (const attr of Array.from(el.attributes)) {
967
+ const name = attr.name.toLowerCase();
968
+ if (name === "href") continue;
969
+ if (name.startsWith("on")) continue;
970
+ if (/^javascript:/i.test(attr.value.trim())) continue;
971
+ out.push(`${name}="${escapeAttr(attr.value)}"`);
972
+ }
973
+ return out;
974
+ }
975
+ var HARD_BREAK = " \n";
976
+ function edgeWhitespace(ws) {
977
+ if (!ws) return "";
978
+ return /\n/.test(ws) ? HARD_BREAK : " ";
979
+ }
980
+ function wrapEmphasis(inner, delimiter) {
981
+ const core = inner.trim();
982
+ if (!core) return "";
983
+ const lead = edgeWhitespace(inner.slice(0, inner.length - inner.trimStart().length));
984
+ const trail = edgeWhitespace(inner.slice(inner.trimEnd().length));
985
+ return lead + delimiter + core + delimiter + trail;
986
+ }
987
+ function joinInline(out, piece) {
988
+ if (!piece) return out;
989
+ const outTail = /\s*$/.exec(out)[0];
990
+ const pieceHead = /^\s*/.exec(piece)[0];
991
+ if (!outTail || !pieceHead) return out + piece;
992
+ const merged = /\n/.test(outTail + pieceHead) ? HARD_BREAK : " ";
993
+ return out.slice(0, out.length - outTail.length) + merged + piece.slice(pieceHead.length);
994
+ }
919
995
  function serializeInline(node) {
920
996
  let out = "";
921
997
  node.childNodes.forEach((child) => {
922
- out += serializeInlineNode(child);
998
+ out = joinInline(out, serializeInlineNode(child));
923
999
  });
924
1000
  return out;
925
1001
  }
@@ -936,11 +1012,11 @@ function serializeInlineNode(child) {
936
1012
  switch (tag) {
937
1013
  case "strong":
938
1014
  case "b":
939
- out += inner.trim() ? `**${inner.trim()}**` : "";
1015
+ out += wrapEmphasis(inner, "**");
940
1016
  break;
941
1017
  case "em":
942
1018
  case "i":
943
- out += inner.trim() ? `*${inner.trim()}*` : "";
1019
+ out += wrapEmphasis(inner, "*");
944
1020
  break;
945
1021
  case "code":
946
1022
  out += "`" + inner + "`";
@@ -950,12 +1026,9 @@ function serializeInlineNode(child) {
950
1026
  break;
951
1027
  case "a": {
952
1028
  const href = el.getAttribute("href") || "";
953
- const ref = el.getAttribute("data-cms-ref");
954
- if (ref) {
955
- const attrs = [`href="${escapeAttr(href)}"`, `data-cms-ref="${escapeAttr(ref)}"`];
956
- const label = el.getAttribute("data-cms-ref-label");
957
- if (label) attrs.push(`data-cms-ref-label="${escapeAttr(label)}"`);
958
- out += `<a ${attrs.join(" ")}>${inner}</a>`;
1029
+ const extras = preservedLinkAttrs(el);
1030
+ if (extras.length > 0) {
1031
+ out += `<a ${[`href="${escapeAttr(href)}"`, ...extras].join(" ")}>${inner}</a>`;
959
1032
  } else {
960
1033
  out += href ? `[${inner}](${href})` : inner;
961
1034
  }
@@ -1090,6 +1163,9 @@ function injectStyles() {
1090
1163
  getConnectedToken,
1091
1164
  htmlToMarkdown,
1092
1165
  initInlineEdit,
1093
- rescanFields
1166
+ positionPopover,
1167
+ renderCurrentRefLine,
1168
+ rescanFields,
1169
+ resolveExistingRef
1094
1170
  });
1095
1171
  //# sourceMappingURL=index.cjs.map