tldraw/packages/ui/src/lib/hooks/useInsertMedia.ts
Steve Ruiz 0cc91eec62
ExternalContentManager for handling external content (files, images, etc) (#1550)
This PR improves the editor's APIs around creating assets and files.
This allows end user developers to replace behavior that might occur,
for example, when pasting images or dragging files onto the canvas.

Here, we:
- remove `onCreateAssetFromFile` prop
- remove `onCreateBookmarkFromUrl` prop
- introduce `onEditorReady` prop
- introduce `onEditorWillDispose` prop
- introduce `ExternalContentManager`

The `ExternalContentManager` (ECM) is used in circumstances where we're
turning external content (text, images, urls, etc) into assets or
shapes. It is designed to allow certain methods to be overwritten by
other developers as a kind of weakly supported hack.

For example, when a user drags an image onto the canvas, the event
handler passes a `TLExternalContent` object to the editor's
`putExternalContent` method. This method runs the ECM's handler for this
content type. That handler may in turn run other methods, such as
`createAssetFromFile` or `createShapesForAssets`, which will lead to the
image being created on the canvas.

If a developer wanted to change the way that assets are created from
files, then they could overwrite that method at runtime.

```ts
const handleEditorReady = (editor: Editor) => {
  editor.externalContentManager.createAssetFromFile = myHandler
}

function Example() {
  return <Tldraw onEditorReady={handleEditorReady}/>
}
```

If you wanted to go even deeper, you could override the editor's
`putExternalContent` method.

```ts
const handleEditorReady = (editor: Editor) => {
const handleExternalContent = (info: TLExternalContent): Promise<void> => {
	if (info.type === 'files') {
	   // do something here
	} else {
          // do the normal thing
          editor.externalContentManager.handleContent(info)
        }
}
```

### Change Type

- [x] `major`

### Test Plan

1. Drag images, urls, etc. onto the canvas
2. Use copy and paste for single and multiple files
3. Use bookmark / embed shapes and convert between eachother

### Release Notes

- [editor] add `ExternalContentManager` for plopping content onto the
canvas
- [editor] remove `onCreateAssetFromFile` prop
- [editor] remove `onCreateBookmarkFromUrl` prop
- [editor] introduce `onEditorReady` prop
- [editor] introduce `onEditorWillDispose` prop
- [editor] introduce `ExternalContentManager`
2023-06-08 14:53:11 +00:00

35 lines
983 B
TypeScript

import { ACCEPTED_ASSET_TYPE, useEditor } from '@tldraw/editor'
import { useCallback, useEffect, useRef } from 'react'
export function useInsertMedia() {
const editor = useEditor()
const inputRef = useRef<HTMLInputElement>()
useEffect(() => {
const input = window.document.createElement('input')
input.type = 'file'
input.accept = ACCEPTED_ASSET_TYPE
input.multiple = true
inputRef.current = input
async function onchange(e: Event) {
const fileList = (e.target as HTMLInputElement).files
if (!fileList || fileList.length === 0) return
await editor.putExternalContent({
type: 'files',
files: Array.from(fileList),
point: editor.viewportPageBounds.center,
ignoreParent: false,
})
input.value = ''
}
input.addEventListener('change', onchange)
return () => {
inputRef.current = undefined
input.removeEventListener('change', onchange)
}
}, [editor])
return useCallback(() => {
inputRef.current?.click()
}, [inputRef])
}