/** * Click-to-copy badge — copies text to clipboard with visual feedback. */ import { resolveHost } from '../core/app-dom.js'; const COPY_SELECTOR = '.badge[data-copy="true"]'; const COPIED_CLASS = 'is-copied'; const getCopyValue = (badge) => { const explicit = badge.getAttribute('data-copy-value'); if (explicit !== null && explicit !== '') { return explicit; } return (badge.textContent || '').trim(); }; const copyText = async (value) => { if (!value) { return false; } if (navigator.clipboard?.writeText) { await navigator.clipboard.writeText(value); return true; } const textarea = document.createElement('textarea'); textarea.value = value; textarea.setAttribute('readonly', 'true'); textarea.style.position = 'fixed'; textarea.style.opacity = '0'; document.body.appendChild(textarea); textarea.select(); const ok = document.execCommand('copy'); document.body.removeChild(textarea); return ok; }; export function initBadgeCopy(root = document, options = {}) { const selector = String(options.selector || COPY_SELECTOR).trim() || COPY_SELECTOR; const host = resolveHost(root); const badges = Array.from(host.querySelectorAll(selector)); if (!badges.length) { return { destroy: () => {} }; } const cleanupFns = []; const timers = []; const enhanceBadge = (badge) => { if (badge.dataset.badgeCopyBound === '1') { return; } badge.dataset.badgeCopyBound = '1'; if (!badge.querySelector('.badge-copy-icon')) { const icon = document.createElement('span'); icon.className = 'badge-copy-icon'; icon.setAttribute('aria-hidden', 'true'); icon.innerHTML = ''; badge.appendChild(icon); } badge.setAttribute('role', 'button'); badge.setAttribute('tabindex', '0'); if (!badge.hasAttribute('aria-label')) { badge.setAttribute('aria-label', 'Copy to clipboard'); } const handler = async (event) => { event.preventDefault(); const value = getCopyValue(badge); const ok = await copyText(value); if (!ok) { return; } badge.classList.add(COPIED_CLASS); const timer = window.setTimeout(() => badge.classList.remove(COPIED_CLASS), 1200); timers.push(timer); }; const onKeyDown = (event) => { if (event.key === 'Enter' || event.key === ' ') { handler(event); } }; badge.addEventListener('click', handler); badge.addEventListener('keydown', onKeyDown); cleanupFns.push(() => { badge.removeEventListener('click', handler); badge.removeEventListener('keydown', onKeyDown); delete badge.dataset.badgeCopyBound; }); }; badges.forEach((badge) => { if (badge instanceof HTMLElement) { enhanceBadge(badge); } }); const destroy = () => { cleanupFns.forEach((cleanup) => cleanup()); timers.forEach((timer) => window.clearTimeout(timer)); }; return { destroy }; } export const initCopyBadge = initBadgeCopy;