Browse Source

fix: preserve line breaks when pasting text in editor

imwald
codytseng 8 months ago
parent
commit
2193c92f16
  1. 51
      src/components/PostEditor/PostTextarea/ClipboardAndDropHandler.ts
  2. 6
      src/components/PostEditor/PostTextarea/index.tsx

51
src/components/PostEditor/PostTextarea/FileHandler.ts → src/components/PostEditor/PostTextarea/ClipboardAndDropHandler.ts

@ -11,14 +11,14 @@ const DRAGOVER_CLASS_LIST = [
'rounded-md' 'rounded-md'
] ]
export interface FileHandlerOptions { export interface ClipboardAndDropHandlerOptions {
onUploadStart?: (file: File) => void onUploadStart?: (file: File) => void
onUploadSuccess?: (file: File, result: any) => void onUploadSuccess?: (file: File, result: any) => void
onUploadError?: (file: File, error: any) => void onUploadError?: (file: File, error: any) => void
} }
export const FileHandler = Extension.create<FileHandlerOptions>({ export const ClipboardAndDropHandler = Extension.create<ClipboardAndDropHandlerOptions>({
name: 'fileHandler', name: 'clipboardAndDropHandler',
addOptions() { addOptions() {
return { return {
@ -61,17 +61,38 @@ export const FileHandler = Extension.create<FileHandlerOptions>({
}, },
handlePaste(view, event) { handlePaste(view, event) {
const items = Array.from(event.clipboardData?.items ?? []) const items = Array.from(event.clipboardData?.items ?? [])
const mediaItem = items.find( let handled = false
(item) => item.type.includes('image') || item.type.includes('video')
) for (const item of items) {
if (!mediaItem) return false if (
item.kind === 'file' &&
const file = mediaItem.getAsFile() (item.type.includes('image') || item.type.includes('video'))
if (!file) return false ) {
const file = item.getAsFile()
uploadFile(view, file, options) if (file) {
uploadFile(view, file, options)
return true handled = true
}
} else if (item.kind === 'string' && item.type === 'text/plain') {
item.getAsString((text) => {
const { schema } = view.state
const parts = text.split('\n')
const nodes = []
for (let i = 0; i < parts.length; i++) {
if (i > 0) nodes.push(schema.nodes.hardBreak.create())
if (parts[i]) nodes.push(schema.text(parts[i]))
}
const fragment = schema.nodes.paragraph.create(null, nodes)
const tr = view.state.tr.replaceSelectionWith(fragment)
view.dispatch(tr)
})
handled = true
}
// Only handle the first file/string item
if (handled) break
}
return handled
} }
} }
}) })
@ -79,7 +100,7 @@ export const FileHandler = Extension.create<FileHandlerOptions>({
} }
}) })
async function uploadFile(view: EditorView, file: File, options: FileHandlerOptions) { async function uploadFile(view: EditorView, file: File, options: ClipboardAndDropHandlerOptions) {
const name = file.name const name = file.name
options.onUploadStart?.(file) options.onUploadStart?.(file)

6
src/components/PostEditor/PostTextarea/index.tsx

@ -2,6 +2,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { parseEditorJsonToText } from '@/lib/tiptap' import { parseEditorJsonToText } from '@/lib/tiptap'
import postContentCache from '@/services/post-content-cache.service' import postContentCache from '@/services/post-content-cache.service'
import Document from '@tiptap/extension-document' import Document from '@tiptap/extension-document'
import { HardBreak } from '@tiptap/extension-hard-break'
import History from '@tiptap/extension-history' import History from '@tiptap/extension-history'
import Paragraph from '@tiptap/extension-paragraph' import Paragraph from '@tiptap/extension-paragraph'
import Placeholder from '@tiptap/extension-placeholder' import Placeholder from '@tiptap/extension-placeholder'
@ -12,8 +13,8 @@ import { Event } from 'nostr-tools'
import { Dispatch, forwardRef, SetStateAction, useImperativeHandle } from 'react' import { Dispatch, forwardRef, SetStateAction, useImperativeHandle } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { usePostEditor } from '../PostEditorProvider' import { usePostEditor } from '../PostEditorProvider'
import { ClipboardAndDropHandler } from './ClipboardAndDropHandler'
import CustomMention from './CustomMention' import CustomMention from './CustomMention'
import { FileHandler } from './FileHandler'
import Preview from './Preview' import Preview from './Preview'
import suggestion from './suggestion' import suggestion from './suggestion'
@ -40,13 +41,14 @@ const PostTextarea = forwardRef<
Paragraph, Paragraph,
Text, Text,
History, History,
HardBreak,
Placeholder.configure({ Placeholder.configure({
placeholder: t('Write something...') + ' (' + t('Paste or drop media files to upload') + ')' placeholder: t('Write something...') + ' (' + t('Paste or drop media files to upload') + ')'
}), }),
CustomMention.configure({ CustomMention.configure({
suggestion suggestion
}), }),
FileHandler.configure({ ClipboardAndDropHandler.configure({
onUploadStart: () => setUploadingFiles((prev) => prev + 1), onUploadStart: () => setUploadingFiles((prev) => prev + 1),
onUploadSuccess: () => setUploadingFiles((prev) => prev - 1), onUploadSuccess: () => setUploadingFiles((prev) => prev - 1),
onUploadError: () => setUploadingFiles((prev) => prev - 1) onUploadError: () => setUploadingFiles((prev) => prev - 1)

Loading…
Cancel
Save