You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

clipboard.ts 3.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. import { getShapeUtils } from './shape-utils'
  2. import { Data, Shape } from 'types'
  3. import { getCommonBounds, getSelectedShapes } from 'utils/utils'
  4. import state from './state'
  5. class Clipboard {
  6. current: string
  7. fallback = false
  8. copy = (shapes: Shape[], onComplete?: () => void) => {
  9. this.current = JSON.stringify({ id: 'tldr', shapes })
  10. navigator.permissions.query({ name: 'clipboard-write' }).then((result) => {
  11. if (result.state == 'granted' || result.state == 'prompt') {
  12. navigator.clipboard.writeText(this.current).then(onComplete, () => {
  13. console.warn('Error, could not copy to clipboard. Fallback?')
  14. this.fallback = true
  15. })
  16. } else {
  17. this.fallback = true
  18. }
  19. })
  20. }
  21. paste = () => {
  22. navigator.clipboard
  23. .readText()
  24. .then(this.sendPastedTextToState, this.sendPastedTextToState)
  25. }
  26. sendPastedTextToState(text = this.current) {
  27. if (text === undefined) return
  28. try {
  29. const clipboardData = JSON.parse(text)
  30. state.send('PASTED_SHAPES_FROM_CLIPBOARD', {
  31. shapes: clipboardData.shapes,
  32. })
  33. } catch (e) {
  34. // The text wasn't valid JSON, or it wasn't ours, so paste it as a text object
  35. state.send('PASTED_TEXT_FROM_CLIPBOARD', { text })
  36. }
  37. }
  38. clear = () => {
  39. this.current = undefined
  40. }
  41. copySelectionToSvg(data: Data) {
  42. const shapes = getSelectedShapes(data)
  43. const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
  44. shapes
  45. .sort((a, b) => a.childIndex - b.childIndex)
  46. .forEach((shape) => {
  47. const group = document.getElementById(shape.id + '-group')
  48. const node = document.getElementById(shape.id)
  49. const groupClone = group.cloneNode()
  50. groupClone.appendChild(node.cloneNode(true))
  51. svg.appendChild(groupClone)
  52. })
  53. const bounds = getCommonBounds(
  54. ...shapes.map((shape) => getShapeUtils(shape).getBounds(shape))
  55. )
  56. // No content
  57. if (!bounds) return
  58. const padding = 16
  59. // Resize the element to the bounding box
  60. svg.setAttribute(
  61. 'viewBox',
  62. [
  63. bounds.minX - padding,
  64. bounds.minY - padding,
  65. bounds.width + padding * 2,
  66. bounds.height + padding * 2,
  67. ].join(' ')
  68. )
  69. svg.setAttribute('width', String(bounds.width))
  70. svg.setAttribute('height', String(bounds.height))
  71. // Take a snapshot of the element
  72. const s = new XMLSerializer()
  73. const svgString = s.serializeToString(svg)
  74. // Copy to clipboard!
  75. try {
  76. navigator.clipboard.writeText(svgString)
  77. } catch (e) {
  78. Clipboard.copyStringToClipboard(svgString)
  79. }
  80. }
  81. static copyStringToClipboard(string: string) {
  82. let result: boolean | null
  83. const textarea = document.createElement('textarea')
  84. textarea.setAttribute('position', 'fixed')
  85. textarea.setAttribute('top', '0')
  86. textarea.setAttribute('readonly', 'true')
  87. textarea.setAttribute('contenteditable', 'true')
  88. textarea.style.position = 'fixed'
  89. textarea.value = string
  90. document.body.appendChild(textarea)
  91. textarea.focus()
  92. textarea.select()
  93. try {
  94. const range = document.createRange()
  95. range.selectNodeContents(textarea)
  96. const sel = window.getSelection()
  97. sel.removeAllRanges()
  98. sel.addRange(range)
  99. textarea.setSelectionRange(0, textarea.value.length)
  100. result = document.execCommand('copy')
  101. } catch (err) {
  102. result = null
  103. } finally {
  104. document.body.removeChild(textarea)
  105. }
  106. if (!result) return false
  107. return true
  108. }
  109. }
  110. export default new Clipboard()