feat: replace icon fonts with inline SVGs for code copy button and add "Copied" toast notification
This commit is contained in:
1 parent
1349d3b3dc
commit
c0e40c7362
2 files changed
+50
-13
No files matched your search
+28
-9
@@ -1648,9 +1648,8 @@ footer.footer-modern a:hover {
|
|||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
display: inline-flex;
|
display: grid;
|
||||||
align-items: center;
|
place-items: center;
|
||||||
justify-content: center;
|
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
background: var(--bg-subtle);
|
background: var(--bg-subtle);
|
||||||
@@ -1677,14 +1676,34 @@ footer.footer-modern a:hover {
|
|||||||
border-color: var(--accent);
|
border-color: var(--accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.code-copy i {
|
.code-copy svg {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* "Copied" toast — pops up just left of the button */
|
||||||
|
.code-copy-toast {
|
||||||
|
position: absolute;
|
||||||
|
top: 0.5rem;
|
||||||
|
right: calc(0.5rem + 32px + 8px); /* clears the 32px button + gap */
|
||||||
|
height: 32px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
padding: 0 0.6rem;
|
||||||
width: 100%;
|
border-radius: var(--radius-sm);
|
||||||
height: 100%;
|
background: var(--accent);
|
||||||
font-size: 0.95rem;
|
color: var(--accent-contrast);
|
||||||
line-height: 1;
|
font-size: var(--font-size-xs);
|
||||||
|
font-weight: 600;
|
||||||
|
white-space: nowrap;
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(6px);
|
||||||
|
transition: opacity 0.15s ease, transform 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-copy-toast.is-visible {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Touch devices have no hover — keep the button discoverable */
|
/* Touch devices have no hover — keep the button discoverable */
|
||||||
|
|||||||
+22
-4
@@ -561,6 +561,16 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inline SVGs — centred in their own viewBox, so no icon-font baseline /
|
||||||
|
// side-bearing offsets to fight.
|
||||||
|
var SVG_COPY = '<svg viewBox="0 0 24 24" width="15" height="15" fill="none" ' +
|
||||||
|
'stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" ' +
|
||||||
|
'aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2"/>' +
|
||||||
|
'<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>';
|
||||||
|
var SVG_CHECK = '<svg viewBox="0 0 24 24" width="15" height="15" fill="none" ' +
|
||||||
|
'stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" ' +
|
||||||
|
'aria-hidden="true"><path d="M20 6L9 17l-5-5"/></svg>';
|
||||||
|
|
||||||
function enhance(pre) {
|
function enhance(pre) {
|
||||||
// Skip if already wrapped
|
// Skip if already wrapped
|
||||||
if (pre.parentNode && pre.parentNode.classList &&
|
if (pre.parentNode && pre.parentNode.classList &&
|
||||||
@@ -575,7 +585,12 @@
|
|||||||
btn.type = 'button';
|
btn.type = 'button';
|
||||||
btn.className = 'code-copy';
|
btn.className = 'code-copy';
|
||||||
btn.setAttribute('aria-label', T.copyCode);
|
btn.setAttribute('aria-label', T.copyCode);
|
||||||
btn.innerHTML = '<i class="bi bi-clipboard" aria-hidden="true"></i>';
|
btn.innerHTML = SVG_COPY;
|
||||||
|
|
||||||
|
// "Copied" toast that pops up next to the button
|
||||||
|
var toast = document.createElement('span');
|
||||||
|
toast.className = 'code-copy-toast';
|
||||||
|
toast.textContent = T.copied;
|
||||||
|
|
||||||
var resetTimer = null;
|
var resetTimer = null;
|
||||||
btn.addEventListener('click', function () {
|
btn.addEventListener('click', function () {
|
||||||
@@ -583,18 +598,21 @@
|
|||||||
var text = (code ? code.textContent : pre.textContent) || '';
|
var text = (code ? code.textContent : pre.textContent) || '';
|
||||||
copyText(text).then(function () {
|
copyText(text).then(function () {
|
||||||
btn.classList.add('is-copied');
|
btn.classList.add('is-copied');
|
||||||
btn.innerHTML = '<i class="bi bi-check-lg" aria-hidden="true"></i>';
|
btn.innerHTML = SVG_CHECK;
|
||||||
btn.setAttribute('aria-label', T.copied);
|
btn.setAttribute('aria-label', T.copied);
|
||||||
|
toast.classList.add('is-visible');
|
||||||
clearTimeout(resetTimer);
|
clearTimeout(resetTimer);
|
||||||
resetTimer = setTimeout(function () {
|
resetTimer = setTimeout(function () {
|
||||||
btn.classList.remove('is-copied');
|
btn.classList.remove('is-copied');
|
||||||
btn.innerHTML = '<i class="bi bi-clipboard" aria-hidden="true"></i>';
|
btn.innerHTML = SVG_COPY;
|
||||||
btn.setAttribute('aria-label', T.copyCode);
|
btn.setAttribute('aria-label', T.copyCode);
|
||||||
}, 1800);
|
toast.classList.remove('is-visible');
|
||||||
|
}, 1000);
|
||||||
}).catch(function () {});
|
}).catch(function () {});
|
||||||
});
|
});
|
||||||
|
|
||||||
wrap.appendChild(btn);
|
wrap.appendChild(btn);
|
||||||
|
wrap.appendChild(toast);
|
||||||
}
|
}
|
||||||
|
|
||||||
var pres = document.querySelectorAll('.content pre, .tab-panel pre');
|
var pres = document.querySelectorAll('.content pre, .tab-panel pre');
|
||||||
|
|||||||
Reference in new issue
Block a user