Skip to content

Commit 544fb55

Browse files
committed
fix(mdviewer): add remove link button, move close to top-right in link popover
- Add trash icon remove link button in edit mode - Move close (×) to top-right corner of edit popover - Use trash icon for unlink in view mode - Fix unlink by directly unwrapping <a> instead of execCommand("unlink")
1 parent e259652 commit 544fb55

2 files changed

Lines changed: 67 additions & 11 deletions

File tree

src-mdviewer/src/components/link-popover.js

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,13 @@ function buildPopover() {
7373
const unlinkBtn = document.createElement("button");
7474
unlinkBtn.className = "toolbar-btn link-popover-unlink-btn";
7575
unlinkBtn.setAttribute("aria-label", t("link.remove_link"));
76-
unlinkBtn.innerHTML = "&times;";
76+
unlinkBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/></svg>';
7777
unlinkBtn.addEventListener("mousedown", (e) => e.preventDefault());
7878
unlinkBtn.addEventListener("click", () => {
79-
restoreSelection();
80-
document.execCommand("unlink", false, null);
79+
if (currentAnchor && currentAnchor.parentNode) {
80+
const text = document.createTextNode(currentAnchor.textContent);
81+
currentAnchor.parentNode.replaceChild(text, currentAnchor);
82+
}
8183
hide();
8284
contentEl?.focus({ preventScroll: true });
8385
contentEl?.dispatchEvent(new Event("input", { bubbles: true }));
@@ -91,6 +93,18 @@ function buildPopover() {
9193
editDiv.className = "link-popover-edit";
9294
editDiv.style.display = "none";
9395

96+
// Top bar with close button
97+
const topBar = document.createElement("div");
98+
topBar.className = "link-popover-top-bar";
99+
const cancelBtn = document.createElement("button");
100+
cancelBtn.className = "toolbar-btn link-popover-cancel-btn";
101+
cancelBtn.setAttribute("aria-label", t("link.cancel"));
102+
cancelBtn.innerHTML = "&times;";
103+
cancelBtn.addEventListener("mousedown", (e) => e.preventDefault());
104+
cancelBtn.addEventListener("click", () => cancelEdit());
105+
topBar.appendChild(cancelBtn);
106+
editDiv.appendChild(topBar);
107+
94108
// Text row
95109
const textRow = document.createElement("div");
96110
textRow.className = "link-popover-edit-row";
@@ -128,16 +142,28 @@ function buildPopover() {
128142
confirmBtn.addEventListener("click", () => applyLink());
129143
urlRow.appendChild(confirmBtn);
130144

131-
const cancelBtn = document.createElement("button");
132-
cancelBtn.className = "toolbar-btn link-popover-cancel-btn";
133-
cancelBtn.setAttribute("aria-label", t("link.cancel"));
134-
cancelBtn.innerHTML = "&times;";
135-
cancelBtn.addEventListener("mousedown", (e) => e.preventDefault());
136-
cancelBtn.addEventListener("click", () => cancelEdit());
137-
urlRow.appendChild(cancelBtn);
138-
139145
editDiv.appendChild(urlRow);
140146

147+
// Remove link button
148+
const removeRow = document.createElement("div");
149+
removeRow.className = "link-popover-remove-row";
150+
const removeBtn = document.createElement("button");
151+
removeBtn.className = "toolbar-btn link-popover-remove-btn";
152+
removeBtn.innerHTML = `<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/></svg> <span>${t("link.remove_link")}</span>`;
153+
removeBtn.addEventListener("mousedown", (e) => e.preventDefault());
154+
removeBtn.addEventListener("click", () => {
155+
// Remove the link by unwrapping the <a> — replace it with its text content
156+
if (currentAnchor && currentAnchor.parentNode) {
157+
const text = document.createTextNode(currentAnchor.textContent);
158+
currentAnchor.parentNode.replaceChild(text, currentAnchor);
159+
}
160+
hide();
161+
contentEl?.focus({ preventScroll: true });
162+
contentEl?.dispatchEvent(new Event("input", { bubbles: true }));
163+
});
164+
removeRow.appendChild(removeBtn);
165+
editDiv.appendChild(removeRow);
166+
141167
const handleKeydown = (e) => {
142168
if (e.key === "Enter") {
143169
e.preventDefault();

src-mdviewer/src/styles/editor.css

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,36 @@
304304
display: flex;
305305
flex-direction: column;
306306
gap: var(--space-xs);
307+
position: relative;
308+
}
309+
310+
.link-popover-top-bar {
311+
display: flex;
312+
justify-content: flex-end;
313+
position: absolute;
314+
top: -2px;
315+
right: -4px;
316+
}
317+
318+
.link-popover-remove-row {
319+
display: flex;
320+
justify-content: flex-end;
321+
margin-top: 2px;
322+
}
323+
324+
.link-popover-remove-btn {
325+
display: flex;
326+
align-items: center;
327+
gap: 4px;
328+
width: auto !important;
329+
padding: 2px 8px !important;
330+
font-size: var(--font-size-xs);
331+
color: var(--color-alert-caution);
332+
border-radius: var(--radius-sm);
333+
}
334+
335+
.link-popover-remove-btn:hover {
336+
background: rgba(255, 50, 50, 0.1);
307337
}
308338

309339
.link-popover-edit-row {

0 commit comments

Comments
 (0)