tldraw/state/clipboard.ts

161 lines
4 KiB
TypeScript
Raw Normal View History

2021-06-21 21:35:28 +00:00
import { getShapeUtils } from './shape-utils'
2021-06-18 10:14:23 +00:00
import { Data, Shape } from 'types'
2021-06-29 12:00:59 +00:00
import { getCommonBounds } from 'utils'
import tld from 'utils/tld'
2021-06-18 10:14:23 +00:00
import state from './state'
class Clipboard {
current: string
fallback = false
copy = (shapes: Shape[], onComplete?: () => void) => {
if (shapes === undefined) return
2021-06-18 10:14:23 +00:00
this.current = JSON.stringify({ id: 'tldr', shapes })
if ('permissions' in navigator && 'clipboard' in navigator) {
navigator.permissions
.query({ name: 'clipboard-write' })
.then((result) => {
if (result.state == 'granted' || result.state == 'prompt') {
navigator.clipboard.writeText(this.current).then(onComplete, () => {
console.warn('Error, could not copy to clipboard. Fallback?')
this.fallback = true
})
} else {
this.fallback = true
}
2021-06-18 10:14:23 +00:00
})
}
2021-06-18 10:14:23 +00:00
}
paste = () => {
try {
navigator.clipboard.readText().then(this.sendPastedTextToState)
} catch (e) {
this.fallback = true
}
return this
2021-06-18 10:14:23 +00:00
}
sendPastedTextToState = (text = this.current) => {
2021-06-18 10:14:23 +00:00
if (text === undefined) return
try {
const clipboardData = JSON.parse(text)
2021-06-18 10:14:23 +00:00
state.send('PASTED_SHAPES_FROM_CLIPBOARD', {
shapes: clipboardData.shapes,
})
} catch (e) {
// The text wasn't valid JSON, or it wasn't ours, so paste it as a text object
state.send('PASTED_TEXT_FROM_CLIPBOARD', { text })
}
return this
2021-06-18 10:14:23 +00:00
}
clear = () => {
this.current = undefined
return this
2021-06-18 10:14:23 +00:00
}
2021-06-20 22:01:40 +00:00
copySelectionToSvg(data: Data) {
2021-06-29 12:00:59 +00:00
const shapes = tld.getSelectedShapes(data)
const shapesToCopy = shapes.length > 0 ? shapes : tld.getShapes(data)
2021-06-20 22:01:40 +00:00
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
if (shapesToCopy.length === 0) return
shapesToCopy
2021-06-20 22:01:40 +00:00
.sort((a, b) => a.childIndex - b.childIndex)
.forEach((shape) => {
2021-07-09 22:35:38 +00:00
const group = document.getElementById(shape.id)
2021-06-20 22:01:40 +00:00
2021-07-09 22:35:38 +00:00
const groupClone = group.cloneNode(true)
2021-07-09 22:35:38 +00:00
// TODO: Add children if the shape is a group
2021-06-20 22:01:40 +00:00
svg.appendChild(groupClone)
})
const bounds = getCommonBounds(
...shapesToCopy.map((shape) => getShapeUtils(shape).getBounds(shape))
2021-06-20 22:01:40 +00:00
)
// No content
if (!bounds) return
const padding = 16
// Resize the element to the bounding box
svg.setAttribute(
'viewBox',
[
bounds.minX - padding,
bounds.minY - padding,
bounds.width + padding * 2,
bounds.height + padding * 2,
].join(' ')
)
svg.setAttribute('width', String(bounds.width))
svg.setAttribute('height', String(bounds.height))
// Take a snapshot of the element
const s = new XMLSerializer()
2021-07-01 22:11:09 +00:00
const svgString = s
.serializeToString(svg)
.replaceAll('
 ', '')
.replaceAll(/((\s|")[0-9]*\.[0-9]{2})([0-9]*)(\b|"|\))/g, '$1')
2021-06-20 22:01:40 +00:00
// Copy to clipboard!
try {
navigator.clipboard.writeText(svgString)
} catch (e) {
2021-06-24 12:34:43 +00:00
this.copyStringToClipboard(svgString)
2021-06-20 22:01:40 +00:00
}
return this
2021-06-20 22:01:40 +00:00
}
2021-06-24 12:34:43 +00:00
copyStringToClipboard = (string: string) => {
2021-06-20 22:01:40 +00:00
try {
2021-07-04 18:54:10 +00:00
navigator.clipboard.writeText(string)
} catch (e) {
const textarea = document.createElement('textarea')
textarea.setAttribute('position', 'fixed')
textarea.setAttribute('top', '0')
textarea.setAttribute('readonly', 'true')
textarea.setAttribute('contenteditable', 'true')
textarea.style.position = 'fixed'
textarea.value = string
document.body.appendChild(textarea)
textarea.focus()
textarea.select()
try {
const range = document.createRange()
range.selectNodeContents(textarea)
const sel = window.getSelection()
sel.removeAllRanges()
sel.addRange(range)
textarea.setSelectionRange(0, textarea.value.length)
} catch (err) {
null // Could not copy to clipboard
} finally {
document.body.removeChild(textarea)
}
2021-06-20 22:01:40 +00:00
}
return this
2021-06-20 22:01:40 +00:00
}
2021-06-18 10:14:23 +00:00
}
export default new Clipboard()