Support dropping plain text and image files - #3429
Support dropping plain text and image files#3429Julia Roldi (juliaroldi) wants to merge 7 commits into
Conversation
|
There was a problem hiding this comment.
Pull request overview
This PR extends RoosterJS Content Model drag-and-drop handling to support dropping external plain text and single image files, while preserving existing HTML drop behavior (including forbidden-element cleanup). It also extracts the plain-text-to-DOM conversion logic into a shared textToFragment utility so paste and drag-and-drop share identical whitespace/tab/multiline handling.
Changes:
- Extend external drop handling to support
text/plaindrops and single-image file drops. - Extract plain-text conversion into
textToFragmentand reuse it from paste and drag-and-drop code paths. - Add/adjust unit tests to validate new drop behaviors and
textToFragmentconversion rules.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/roosterjs-content-model-plugins/lib/dragAndDrop/utils/handleDroppedExternalContent.ts | Adds plain text + single image file drop support by producing a dropped content model from DataTransfer. |
| packages/roosterjs-content-model-plugins/lib/dragAndDrop/DragAndDropPlugin.ts | Routes external drops through the updated handleDroppedExternalContent (no longer pre-filtered by HTML only). |
| packages/roosterjs-content-model-plugins/test/dragAndDrop/utils/handleDroppedExternalContentTest.ts | Updates existing tests and adds coverage for plain text and image file drops. |
| packages/roosterjs-content-model-plugins/test/dragAndDrop/DragAndDropPluginTest.ts | Updates plugin tests to reflect that external drop handler is called regardless of HTML presence. |
| packages/roosterjs-content-model-dom/lib/domUtils/textToFragment.ts | Introduces shared plain-text-to-DocumentFragment conversion utility (whitespace/tab/multiline handling). |
| packages/roosterjs-content-model-dom/lib/index.ts | Re-exports textToFragment from the DOM package barrel. |
| packages/roosterjs-content-model-dom/test/domUtils/textToFragmentTest.ts | Adds unit tests for textToFragment conversion behavior. |
| packages/roosterjs-content-model-core/lib/command/paste/createPasteFragment.ts | Reuses textToFragment for paste-as-plain-text fragment creation (removes duplicated logic). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (types.some(type => type === 'text/html')) { | ||
| const html = dataTransfer.getData('text/html'); | ||
| if (html) { | ||
| const parsedHtml = editor.getDOMCreator().htmlToDOM(html); | ||
| cleanForbiddenElements(parsedHtml, forbiddenElements); | ||
| return domToContentModel(parsedHtml.body, createDomToModelContext()); | ||
| } | ||
| } else if (types.some(type => type === 'text/plain')) { | ||
| const text = dataTransfer.getData('text/plain'); | ||
| if (text) { | ||
| const textFragment = textToFragment(text, editor.getDocument()); | ||
| return domToContentModel(textFragment, createDomToModelContext()); | ||
| } | ||
| } else if (types.some(type => type === 'Files')) { | ||
| const files = dataTransfer.files; | ||
| const file = files?.length === 1 ? files[0] : undefined; | ||
| if (file?.type.startsWith('image/')) { | ||
| const model = createContentModelDocument(); | ||
| const paragraph = createParagraph(); | ||
| paragraph.segments.push(createImage(URL.createObjectURL(file))); | ||
| model.blocks.push(paragraph); | ||
| return model; | ||
| } | ||
| } |
| paragraph.segments.push(createImage(URL.createObjectURL(file))); | ||
| model.blocks.push(paragraph); |
| return undefined; | ||
| } | ||
|
|
||
| if (types.some(type => type === 'text/html')) { |
There was a problem hiding this comment.
Curious, can we reuse the paste code to get model when drop?
There was a problem hiding this comment.
The paste flow currently uses extractClipboardItems to get the transfer item. I’m not sure it’s worth refactoring that code just to reuse a few lines.
Summary
Support dropping external plain text and single image files in the editor while preserving the existing HTML drop behavior and forbidden-element cleanup.
Extract plain-text DOM conversion into the shared
textToFragmentutility so paste and drag-and-drop use the same whitespace, tab, and multiline handling.How to test
yarn test:fast --testPathPattern="textToFragment|DragAndDropPlugin|handleDroppedExternalContent".