@broberg/cms-inline-edit 0.5.5 → 0.6.0
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 +78 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.js +76 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -29,7 +29,8 @@ __export(index_exports, {
|
|
|
29
29
|
positionPopover: () => positionPopover,
|
|
30
30
|
renderCurrentRefLine: () => renderCurrentRefLine,
|
|
31
31
|
rescanFields: () => rescanFields,
|
|
32
|
-
resolveExistingRef: () => resolveExistingRef
|
|
32
|
+
resolveExistingRef: () => resolveExistingRef,
|
|
33
|
+
setLinkTarget: () => setLinkTarget
|
|
33
34
|
});
|
|
34
35
|
module.exports = __toCommonJS(index_exports);
|
|
35
36
|
|
|
@@ -43,6 +44,30 @@ function applyFieldSlice(current, original, next) {
|
|
|
43
44
|
return current.slice(0, first) + next + current.slice(first + original.length);
|
|
44
45
|
}
|
|
45
46
|
|
|
47
|
+
// src/link-target.ts
|
|
48
|
+
function isSchemeless(raw) {
|
|
49
|
+
const v = (raw || "").trim();
|
|
50
|
+
if (!v) return false;
|
|
51
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(v)) return false;
|
|
52
|
+
if (v.startsWith("//")) return false;
|
|
53
|
+
if (v.startsWith("/")) return false;
|
|
54
|
+
if (v.startsWith("#")) return false;
|
|
55
|
+
if (v.startsWith("?")) return false;
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
function withHttps(raw) {
|
|
59
|
+
return `https://${(raw || "").trim().replace(/^\/+/, "")}`;
|
|
60
|
+
}
|
|
61
|
+
function isExternalHost(raw, currentHost) {
|
|
62
|
+
const v = (raw || "").trim();
|
|
63
|
+
if (!/^https?:\/\//i.test(v)) return false;
|
|
64
|
+
try {
|
|
65
|
+
return new URL(v).host.toLowerCase() !== (currentHost || "").toLowerCase();
|
|
66
|
+
} catch {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
46
71
|
// src/token-safe.ts
|
|
47
72
|
var TEXT_NODE = 3;
|
|
48
73
|
var ELEMENT_NODE = 1;
|
|
@@ -97,6 +122,9 @@ var DEFAULT_LABELS = {
|
|
|
97
122
|
linkLiveHint: "Linket peger p\xE5 <b>siden</b> \u2014 ikke p\xE5 en adresse. Flyttes siden, eller f\xE5r den et nyt navn, retter linket sig selv.",
|
|
98
123
|
linkUrlHint: "En fri adresse peger pr\xE6cis d\xE9r, du skriver. Den f\xF8lger <b>ikke</b> med, hvis m\xE5let flytter sig.",
|
|
99
124
|
linkUrl: "Adresse",
|
|
125
|
+
linkNewTab: "\xC5bn i ny fane",
|
|
126
|
+
linkSchemeless: "<b>{v}</b> l\xE6ses som en side p\xE5 <b>dette</b> site \u2014 ikke som en adresse ude p\xE5 nettet.",
|
|
127
|
+
linkSchemelessFix: "Tilf\xF8j https://",
|
|
100
128
|
linkInsert: "Inds\xE6t link",
|
|
101
129
|
linkSave: "Gem \xE6ndring",
|
|
102
130
|
linkCancel: "Annull\xE9r",
|
|
@@ -474,6 +502,8 @@ var savedRange = null;
|
|
|
474
502
|
var linkDialog = null;
|
|
475
503
|
var linkPages = null;
|
|
476
504
|
var linkPicked = null;
|
|
505
|
+
var linkNewTab = false;
|
|
506
|
+
var linkNewTabTouched = false;
|
|
477
507
|
var linkEditing = null;
|
|
478
508
|
function hideLinkDialog() {
|
|
479
509
|
if (linkDialog) linkDialog.style.display = "none";
|
|
@@ -555,9 +585,13 @@ function renderLinkDialog() {
|
|
|
555
585
|
if (!d) return;
|
|
556
586
|
const L = uiLabels;
|
|
557
587
|
const editing = !!linkEditing;
|
|
588
|
+
if (!editing) {
|
|
589
|
+
linkNewTab = false;
|
|
590
|
+
linkNewTabTouched = false;
|
|
591
|
+
}
|
|
558
592
|
const existingRef = linkEditing?.getAttribute("data-cms-ref") ?? "";
|
|
559
593
|
const onPageTab = !editing || !!existingRef;
|
|
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>`;
|
|
594
|
+
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 data-role="schemeless" data-testid="inline-link-schemeless" style="display:none;margin-top:8px;padding:8px 10px;background:rgba(255,176,32,.10);border:1px solid rgba(255,176,32,.34);border-radius:8px"><p data-role="schemeless-text" style="margin:0;font-size:11.5px;color:#e8d6b0;line-height:1.5"></p><button type="button" data-testid="inline-link-add-scheme" data-role="add-scheme" style="${btnCss(false)};margin-top:7px;height:26px;font-size:12px">${L.linkSchemelessFix}</button></div></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><button type="button" data-role="newtab" data-testid="inline-link-newtab" aria-pressed="false" style="display:flex;align-items:center;gap:8px;margin-top:12px;background:none;border:none;padding:0;cursor:pointer;font-family:inherit;font-size:12.5px;color:#c9d1dd"><span data-role="newtab-box" style="${checkboxCss(false)}"></span><span>${L.linkNewTab}</span></button><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>`;
|
|
561
595
|
const q = (role) => d.querySelector(`[data-role="${role}"]`);
|
|
562
596
|
const text = q("text");
|
|
563
597
|
const urlIn = q("url");
|
|
@@ -565,6 +599,8 @@ function renderLinkDialog() {
|
|
|
565
599
|
if (editing) {
|
|
566
600
|
text.value = linkEditing?.getAttribute("data-cms-ref-label") === "auto" ? "" : linkEditing?.textContent ?? "";
|
|
567
601
|
if (!existingRef) urlIn.value = linkEditing?.getAttribute("href") ?? "";
|
|
602
|
+
linkNewTab = linkEditing?.getAttribute("target") === "_blank";
|
|
603
|
+
linkNewTabTouched = true;
|
|
568
604
|
q("remove").appendChild(buildRemoveLink());
|
|
569
605
|
}
|
|
570
606
|
d.querySelectorAll("[data-tab]").forEach((btn) => {
|
|
@@ -584,6 +620,16 @@ function renderLinkDialog() {
|
|
|
584
620
|
urlIn.oninput = syncLinkDialog;
|
|
585
621
|
q("cancel").onclick = hideLinkDialog;
|
|
586
622
|
q("submit").onclick = applyLink;
|
|
623
|
+
q("newtab").onclick = () => {
|
|
624
|
+
linkNewTab = !linkNewTab;
|
|
625
|
+
linkNewTabTouched = true;
|
|
626
|
+
syncLinkDialog();
|
|
627
|
+
};
|
|
628
|
+
q("add-scheme").onclick = () => {
|
|
629
|
+
urlIn.value = withHttps(urlIn.value);
|
|
630
|
+
urlIn.focus();
|
|
631
|
+
syncLinkDialog();
|
|
632
|
+
};
|
|
587
633
|
renderLinkList("");
|
|
588
634
|
syncLinkDialog();
|
|
589
635
|
}
|
|
@@ -689,6 +735,30 @@ function syncLinkDialog() {
|
|
|
689
735
|
q("hint").innerHTML = own ? uiLabels.linkTextOwnHint : onPage ? uiLabels.linkTextAutoHint : uiLabels.linkUrlHint;
|
|
690
736
|
q("live").style.display = onPage ? "flex" : "none";
|
|
691
737
|
q("submit").disabled = onPage ? !linkPicked : !q("url").value.trim();
|
|
738
|
+
const raw = q("url").value;
|
|
739
|
+
const doubtful = !onPage && isSchemeless(raw);
|
|
740
|
+
q("schemeless").style.display = doubtful ? "block" : "none";
|
|
741
|
+
if (doubtful) {
|
|
742
|
+
q("schemeless-text").innerHTML = uiLabels.linkSchemeless.replace(
|
|
743
|
+
"{v}",
|
|
744
|
+
escapeHtml(raw.trim())
|
|
745
|
+
);
|
|
746
|
+
}
|
|
747
|
+
if (!onPage && !linkNewTabTouched) {
|
|
748
|
+
linkNewTab = isExternalHost(raw, typeof location !== "undefined" ? location.host : "");
|
|
749
|
+
}
|
|
750
|
+
const box = q("newtab-box");
|
|
751
|
+
if (box) box.setAttribute("style", checkboxCss(linkNewTab));
|
|
752
|
+
q("newtab").setAttribute("aria-pressed", String(linkNewTab));
|
|
753
|
+
}
|
|
754
|
+
function setLinkTarget(el, newTab) {
|
|
755
|
+
if (newTab) {
|
|
756
|
+
el.setAttribute("target", "_blank");
|
|
757
|
+
el.setAttribute("rel", "noopener");
|
|
758
|
+
} else {
|
|
759
|
+
el.removeAttribute("target");
|
|
760
|
+
el.removeAttribute("rel");
|
|
761
|
+
}
|
|
692
762
|
}
|
|
693
763
|
function applyLink() {
|
|
694
764
|
const d = linkDialog;
|
|
@@ -700,18 +770,21 @@ function applyLink() {
|
|
|
700
770
|
if (!href) return;
|
|
701
771
|
const ref = onPage && linkPicked ? `${linkPicked.collection}:${linkPicked.slug}` : "";
|
|
702
772
|
const label = own || (onPage ? linkPicked?.title ?? href : href);
|
|
773
|
+
const applyTarget = (el) => setLinkTarget(el, linkNewTab);
|
|
703
774
|
if (linkEditing) {
|
|
704
775
|
linkEditing.setAttribute("href", href);
|
|
705
776
|
if (ref) linkEditing.setAttribute("data-cms-ref", ref);
|
|
706
777
|
else linkEditing.removeAttribute("data-cms-ref");
|
|
707
778
|
if (ref && !own) linkEditing.setAttribute("data-cms-ref-label", "auto");
|
|
708
779
|
else linkEditing.removeAttribute("data-cms-ref-label");
|
|
780
|
+
applyTarget(linkEditing);
|
|
709
781
|
linkEditing.textContent = label;
|
|
710
782
|
hideLinkDialog();
|
|
711
783
|
return;
|
|
712
784
|
}
|
|
713
785
|
const a = document.createElement("a");
|
|
714
786
|
a.setAttribute("href", href);
|
|
787
|
+
applyTarget(a);
|
|
715
788
|
if (ref) {
|
|
716
789
|
a.setAttribute("data-cms-ref", ref);
|
|
717
790
|
if (!own) a.setAttribute("data-cms-ref-label", "auto");
|
|
@@ -736,6 +809,7 @@ function escapeHtml(v) {
|
|
|
736
809
|
return v.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
737
810
|
}
|
|
738
811
|
var tabCss = (on) => "flex:1;background:" + (on ? "#2a2f38" : "none") + ";border:1px solid " + (on ? "#3a3f4a" : "transparent") + ";color:" + (on ? "#fff" : "#9aa3b2") + ";height:32px;border-radius:7px;font-size:13px;cursor:pointer;font-weight:500;font-family:inherit;";
|
|
812
|
+
var checkboxCss = (on) => "width:16px;height:16px;flex:0 0 16px;border-radius:4px;box-sizing:border-box;display:inline-block;" + (on ? `background:#00b2ff;border:1px solid #00b2ff;background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M4 8.5l2.6 2.6L12 5.8' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/></svg>");background-size:100% 100%;` : "background:#141821;border:1px solid #3a3f4a;");
|
|
739
813
|
var fieldCss = () => "width:100%;background:#141821;border:1px solid #3a3f4a;color:#fff;height:34px;border-radius:7px;padding:0 10px;font-size:13.5px;outline:none;font-family:inherit;box-sizing:border-box;";
|
|
740
814
|
var labelCss = () => "font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:#9aa3b2;margin:12px 0 5px;display:block;";
|
|
741
815
|
var btnCss = (primary) => "height:33px;border-radius:7px;font-size:13px;cursor:pointer;padding:0 14px;font-weight:600;font-family:inherit;" + (primary ? "background:#00b2ff;color:#04121b;border:none;" : "background:none;border:1px solid #3a3f4a;color:#fff;font-weight:500;");
|
|
@@ -1166,6 +1240,7 @@ function injectStyles() {
|
|
|
1166
1240
|
positionPopover,
|
|
1167
1241
|
renderCurrentRefLine,
|
|
1168
1242
|
rescanFields,
|
|
1169
|
-
resolveExistingRef
|
|
1243
|
+
resolveExistingRef,
|
|
1244
|
+
setLinkTarget
|
|
1170
1245
|
});
|
|
1171
1246
|
//# sourceMappingURL=index.cjs.map
|