Files
upstream-tiptap/packages/extension-file-handler/src/FileHandlePlugin.ts
upstream-mirror 083a46e93b
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
snapshot of https://github.com/ueberdosis/tiptap.git (main, 2026-09-15; blobs > 131072B omitted)
2026-09-15 21:11:02 +05:00

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
},
},
})
}