47 lines
1.4 KiB
TypeScript
47 lines
1.4 KiB
TypeScript
|
|
/**
|
||
|
|
* Copy text to clipboard with a fallback for non-secure contexts (HTTP, non-localhost).
|
||
|
|
* `navigator.clipboard` is only available in secure contexts; intranet HTTP deployments
|
||
|
|
* must fall back to the legacy `document.execCommand('copy')` flow via a hidden textarea.
|
||
|
|
*/
|
||
|
|
export async function copyToClipboard(text: string): Promise<boolean> {
|
||
|
|
if (typeof navigator !== 'undefined' && navigator.clipboard && window.isSecureContext) {
|
||
|
|
try {
|
||
|
|
await navigator.clipboard.writeText(text)
|
||
|
|
return true
|
||
|
|
} catch {
|
||
|
|
// fall through to legacy fallback
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (typeof document === 'undefined') return false
|
||
|
|
|
||
|
|
const textarea = document.createElement('textarea')
|
||
|
|
textarea.value = text
|
||
|
|
textarea.setAttribute('readonly', '')
|
||
|
|
textarea.style.position = 'fixed'
|
||
|
|
textarea.style.top = '0'
|
||
|
|
textarea.style.left = '0'
|
||
|
|
textarea.style.width = '1px'
|
||
|
|
textarea.style.height = '1px'
|
||
|
|
textarea.style.padding = '0'
|
||
|
|
textarea.style.border = 'none'
|
||
|
|
textarea.style.outline = 'none'
|
||
|
|
textarea.style.boxShadow = 'none'
|
||
|
|
textarea.style.background = 'transparent'
|
||
|
|
textarea.style.opacity = '0'
|
||
|
|
document.body.appendChild(textarea)
|
||
|
|
|
||
|
|
let ok = false
|
||
|
|
try {
|
||
|
|
textarea.focus()
|
||
|
|
textarea.select()
|
||
|
|
textarea.setSelectionRange(0, text.length)
|
||
|
|
ok = document.execCommand('copy')
|
||
|
|
} catch {
|
||
|
|
ok = false
|
||
|
|
} finally {
|
||
|
|
document.body.removeChild(textarea)
|
||
|
|
}
|
||
|
|
return ok
|
||
|
|
}
|