Release v0.5.0: dev -> main #288
@@ -586,6 +586,20 @@ a.admin-link:hover { color: var(--fg); text-decoration: underline; }
|
|||||||
border-color: var(--ok);
|
border-color: var(--ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* #260: static-width success feedback. Label and checkmark stack in one
|
||||||
|
grid cell, so the button is always as wide as the wider of the two and
|
||||||
|
never shifts on click. Feedback is a pure .ok class toggle. */
|
||||||
|
.swapbtn {
|
||||||
|
display: inline-grid;
|
||||||
|
}
|
||||||
|
.swapbtn > * {
|
||||||
|
grid-area: 1 / 1;
|
||||||
|
justify-self: center;
|
||||||
|
}
|
||||||
|
.swapbtn .swap-check { visibility: hidden; }
|
||||||
|
.swapbtn.ok .swap-check { visibility: visible; }
|
||||||
|
.swapbtn.ok .swap-label { visibility: hidden; }
|
||||||
|
|
||||||
/* headings: unified treatment (mirrors .side-section h3) */
|
/* headings: unified treatment (mirrors .side-section h3) */
|
||||||
.settings-head h1, .paste-title-bar h1, .head-row h1, .inner h1 {
|
.settings-head h1, .paste-title-bar h1, .head-row h1, .inner h1 {
|
||||||
letter-spacing: -0.01em;
|
letter-spacing: -0.01em;
|
||||||
|
|||||||
@@ -192,18 +192,16 @@ async function create() {
|
|||||||
// button, password auto-unlock, then redirect to the paste.
|
// button, password auto-unlock, then redirect to the paste.
|
||||||
function finishCreate(data) {
|
function finishCreate(data) {
|
||||||
const url = location.origin + '/' + (data.custom_slug || data.id);
|
const url = location.origin + '/' + (data.custom_slug || data.id);
|
||||||
showResult('<a href="' + url + '">' + url + '</a> <button class="btn btn-icon" id="result-copy" title="Copy URL" type="button">⧉</button>', 'ok');
|
// #260 attempt 2: .swapbtn markup — label and checkmark share one grid
|
||||||
|
// cell, so the button width is static and feedback is a class toggle.
|
||||||
|
showResult('<a href="' + url + '">' + url + '</a> <button class="btn btn-icon swapbtn" id="result-copy" title="Copy URL" type="button"><span class="swap-label">Copy</span><span class="swap-check">✓</span></button>', 'ok');
|
||||||
$('result').dataset.token = data.deletion_token || '';
|
$('result').dataset.token = data.deletion_token || '';
|
||||||
const copyBtn = document.getElementById('result-copy');
|
const copyBtn = document.getElementById('result-copy');
|
||||||
copyBtn.addEventListener('click', () => {
|
copyBtn.addEventListener('click', () => {
|
||||||
try {
|
try {
|
||||||
navigator.clipboard.writeText(url);
|
navigator.clipboard.writeText(url);
|
||||||
copyBtn.classList.add('ok'); // in-place success feedback (#53)
|
copyBtn.classList.add('ok'); // in-place success feedback (#53)
|
||||||
// #260: pin the pre-swap width so the wider/narrower glyph never
|
setTimeout(() => copyBtn.classList.remove('ok'), 2000);
|
||||||
// shifts neighbouring elements; release it when the label restores.
|
|
||||||
copyBtn.style.minWidth = Math.ceil(copyBtn.getBoundingClientRect().width) + 'px';
|
|
||||||
copyBtn.textContent = '✓';
|
|
||||||
setTimeout(() => { copyBtn.classList.remove('ok'); copyBtn.textContent = '⧉'; copyBtn.style.minWidth = ''; }, 2000);
|
|
||||||
} catch(e) { toast('Copy failed', 'error'); }
|
} catch(e) { toast('Copy failed', 'error'); }
|
||||||
});
|
});
|
||||||
// token carried via sessionStorage, never in the URL (#143)
|
// token carried via sessionStorage, never in the URL (#143)
|
||||||
|
|||||||
@@ -18,17 +18,12 @@ function toggleStats() {
|
|||||||
}
|
}
|
||||||
function copyFeedback(btn) {
|
function copyFeedback(btn) {
|
||||||
if (!btn) return;
|
if (!btn) return;
|
||||||
if (!btn.dataset.label) btn.dataset.label = btn.textContent; // remember the original label (Copy/Link)
|
// #260 attempt 2: .swapbtn stacks the label and checkmark in the same grid
|
||||||
// #260: keep the button width static during the feedback so surrounding
|
// cell, so the button width is always the wider of the two and never moves.
|
||||||
// elements never jump. Pin the pre-swap width, release it on restore.
|
// Feedback is a pure class toggle; no width pinning, no textContent swap.
|
||||||
if (!btn.dataset.pinned) {
|
|
||||||
btn.style.minWidth = Math.ceil(btn.getBoundingClientRect().width) + 'px';
|
|
||||||
btn.dataset.pinned = '1';
|
|
||||||
}
|
|
||||||
btn.classList.add('ok');
|
btn.classList.add('ok');
|
||||||
btn.textContent = '✓';
|
|
||||||
clearTimeout(btn._okh);
|
clearTimeout(btn._okh);
|
||||||
btn._okh = setTimeout(() => { btn.classList.remove('ok'); btn.textContent = btn.dataset.label; }, 2000);
|
btn._okh = setTimeout(() => btn.classList.remove('ok'), 2000);
|
||||||
}
|
}
|
||||||
function copyContent(btn) {
|
function copyContent(btn) {
|
||||||
navigator.clipboard.writeText(document.getElementById('raw-content').value)
|
navigator.clipboard.writeText(document.getElementById('raw-content').value)
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
<div class="spacer"></div>
|
<div class="spacer"></div>
|
||||||
<button type="button" class="iconbtn wrap-toggle" title="Toggle line wrap" aria-pressed="false">Wrap</button>
|
<button type="button" class="iconbtn wrap-toggle" title="Toggle line wrap" aria-pressed="false">Wrap</button>
|
||||||
<a class="iconbtn" href="/raw/{{.ID}}">Raw</a>
|
<a class="iconbtn" href="/raw/{{.ID}}">Raw</a>
|
||||||
<a class="iconbtn" href="#" id="copy-link-btn">Link</a>
|
<a class="iconbtn swapbtn" href="#" id="copy-link-btn"><span class="swap-label">Link</span><span class="swap-check">✓</span></a>
|
||||||
<a class="iconbtn" href="#" id="copy-btn">Copy</a>
|
<a class="iconbtn swapbtn" href="#" id="copy-btn"><span class="swap-label">Copy</span><span class="swap-check">✓</span></a>
|
||||||
{{if .DeletionToken}}<a class="iconbtn danger" href="#" id="delete-btn">Delete</a>{{end}}
|
{{if .DeletionToken}}<a class="iconbtn danger" href="#" id="delete-btn">Delete</a>{{end}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user