Some checks failed
Verify / Install node dependencies (push) Failing after 2s
Verify / Build packages (push) Has been skipped
Verify / Check linting & formatting (push) Has been skipped
Verify / Run unit tests (push) Has been skipped
Verify / Run e2e tests (shard 1/4) (push) Has been skipped
Verify / Run e2e tests (shard 2/4) (push) Has been skipped
Verify / Run e2e tests (shard 3/4) (push) Has been skipped
Verify / Run e2e tests (shard 4/4) (push) Has been skipped
Publish / Resolve publish config (push) Failing after 2s
Publish / Send notification to Slack (push) Has been skipped
Publish / Send failure notification to Slack (push) Failing after 8s
Verify / Merge e2e reports (push) Has been skipped
Publish / Build (push) Has been skipped
Publish / Bump Versions OR Release to Registry (push) Has been skipped
93 lines
2.3 KiB
TypeScript
93 lines
2.3 KiB
TypeScript
import { Plugin, PluginKey } from '@tiptap/pm/state'
|
|
|
|
import type { FileHandlePluginOptions } from './types.js'
|
|
|
|
export const FileHandlePlugin = ({
|
|
key,
|
|
editor,
|
|
onPaste,
|
|
onDrop,
|
|
allowedMimeTypes,
|
|
consumePasteEvent,
|
|
}: FileHandlePluginOptions) => {
|
|
return new Plugin({
|
|
key: key || new PluginKey('fileHandler'),
|
|
|
|
props: {
|
|
handleDrop(_view, event) {
|
|
if (!onDrop) {
|
|
return false
|
|
}
|
|
|
|
// If the drop contains ProseMirror internal slice data, let ProseMirror
|
|
// handle it — this is an internal content drag (e.g. moving an image),
|
|
// not an external file drop
|
|
if (event.dataTransfer?.types.includes('application/x-prosemirror-slice')) {
|
|
return false
|
|
}
|
|
|
|
if (!event.dataTransfer?.files.length) {
|
|
return false
|
|
}
|
|
|
|
const dropPos = _view.posAtCoords({
|
|
left: event.clientX,
|
|
top: event.clientY,
|
|
})
|
|
|
|
let filesArray = Array.from(event.dataTransfer.files)
|
|
|
|
if (allowedMimeTypes) {
|
|
filesArray = filesArray.filter(file => allowedMimeTypes.includes(file.type))
|
|
}
|
|
|
|
if (filesArray.length === 0) {
|
|
return false
|
|
}
|
|
|
|
event.preventDefault()
|
|
event.stopPropagation()
|
|
|
|
onDrop(editor, filesArray, dropPos?.pos || 0)
|
|
|
|
return true
|
|
},
|
|
|
|
handlePaste(_view, event) {
|
|
if (!onPaste) {
|
|
return false
|
|
}
|
|
|
|
if (!event.clipboardData?.files.length) {
|
|
return false
|
|
}
|
|
|
|
let filesArray = Array.from(event.clipboardData.files)
|
|
const htmlContent = event.clipboardData.getData('text/html')
|
|
|
|
if (allowedMimeTypes) {
|
|
filesArray = filesArray.filter(file => allowedMimeTypes.includes(file.type))
|
|
}
|
|
|
|
if (filesArray.length === 0) {
|
|
return false
|
|
}
|
|
|
|
event.preventDefault()
|
|
event.stopPropagation()
|
|
|
|
onPaste(editor, filesArray, htmlContent)
|
|
|
|
// When HTML content is present alongside files (e.g. animated GIFs
|
|
// copied as PNG), and consumePasteEvent is not set, let other
|
|
// extensions handle the HTML content via their parseHTML/paste rules.
|
|
if (htmlContent.length > 0 && !consumePasteEvent) {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
},
|
|
},
|
|
})
|
|
}
|