a tool for shared writing and social publishing
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

handle pasting with linebreaks!

+88 -47
+80 -45
components/TextBlock/index.tsx
··· 23 23 import { addImage } from "src/utils/addImage"; 24 24 import { BlockProps } from "components/Blocks"; 25 25 import { TextBlockKeymap } from "./keymap"; 26 - import { schema } from "./schema"; 26 + import { multiBlockSchema, schema } from "./schema"; 27 27 import { useUIState } from "src/useUIState"; 28 28 import { CardSmall, ImageSmall } from "components/Icons"; 29 + import { DOMParser as ProsemirrorDOMParser } from "prosemirror-model"; 29 30 30 31 export let useEditorStates = create(() => ({ 31 32 lastXPosition: 0, ··· 122 123 123 124 return ( 124 125 <ProseMirror 126 + handlePaste={(view, e) => { 127 + if (!rep.rep) return; 128 + if (!e.clipboardData) return; 129 + let text = e.clipboardData.getData("text/html"); 130 + let editorState = 131 + useEditorStates.getState().editorStates[props.entityID]; 132 + if (!editorState) return; 133 + const parser = ProsemirrorDOMParser.fromSchema(multiBlockSchema); 134 + let xml = new DOMParser().parseFromString(text, "text/html"); 135 + let nodes = parser.parse(xml); 136 + let currentPosition = propsRef.current.position; 137 + nodes.content.forEach((node, _, index) => { 138 + if (index === 0) return; 139 + console.log(node); 140 + let newEntityID = crypto.randomUUID(); 141 + currentPosition = generateKeyBetween( 142 + propsRef.current.position, 143 + propsRef.current.nextPosition, 144 + ); 145 + console.log(repRef.current); 146 + repRef.current?.mutate.addBlock({ 147 + newEntityID, 148 + parent: propsRef.current.parent, 149 + type: "text", 150 + position: currentPosition, 151 + }); 152 + setTimeout(() => { 153 + let block = useEditorStates.getState().editorStates[newEntityID]; 154 + if (block) { 155 + let tr = block.editor.tr; 156 + let newNode = schema.nodeFromJSON(node.toJSON()); 157 + console.log(newNode); 158 + tr.replaceWith(0, tr.doc.content.size, newNode.content); 159 + let newState = block.editor.apply(tr); 160 + setEditorState(newEntityID, { 161 + editor: newState, 162 + }); 163 + } 164 + }, 10); 165 + }); 166 + 167 + for (let item of e.clipboardData.items) { 168 + if (item?.type.includes("image")) { 169 + let file = item.getAsFile(); 170 + if (file) { 171 + let entity: string; 172 + if (editorState.editor.doc.textContent.length === 0) { 173 + entity = props.entityID; 174 + rep.rep.mutate.assertFact({ 175 + entity: props.entityID, 176 + attribute: "block/type", 177 + data: { type: "block-type-union", value: "image" }, 178 + }); 179 + if (factID) rep.rep.mutate.retractFact({ factID: factID }); 180 + } else { 181 + entity = crypto.randomUUID(); 182 + rep.rep.mutate.addBlock({ 183 + type: "image", 184 + newEntityID: entity, 185 + parent: props.parent, 186 + position: generateKeyBetween( 187 + props.position, 188 + props.nextPosition, 189 + ), 190 + }); 191 + } 192 + addImage(file, rep.rep, { 193 + entityID: entity, 194 + }); 195 + } 196 + return; 197 + } 198 + } 199 + 200 + e.preventDefault(); 201 + e.stopPropagation(); 202 + }} 125 203 mount={mount} 126 204 state={editorState} 127 205 dispatchTransaction={(tr) => { ··· 144 222 onFocus={() => { 145 223 useUIState.getState().setSelectedBlock(props.entityID); 146 224 }} 147 - onKeyDown={(e) => {}} 148 - onPaste={async (e) => { 149 - if (!rep.rep) return; 150 - for (let item of e.clipboardData.items) { 151 - if (item?.type.includes("image")) { 152 - let file = item.getAsFile(); 153 - if (file) { 154 - let editorState = 155 - useEditorStates.getState().editorStates[props.entityID]; 156 - let entity: string; 157 - if ( 158 - editorState && 159 - editorState.editor.doc.textContent.length === 0 160 - ) { 161 - entity = props.entityID; 162 - await rep.rep.mutate.assertFact({ 163 - entity: props.entityID, 164 - attribute: "block/type", 165 - data: { type: "block-type-union", value: "image" }, 166 - }); 167 - if (factID) 168 - await rep.rep.mutate.retractFact({ factID: factID }); 169 - } else { 170 - entity = crypto.randomUUID(); 171 - await rep.rep.mutate.addBlock({ 172 - type: "image", 173 - newEntityID: entity, 174 - parent: props.parent, 175 - position: generateKeyBetween( 176 - props.position, 177 - props.nextPosition, 178 - ), 179 - }); 180 - } 181 - await addImage(file, rep.rep, { 182 - entityID: entity, 183 - }); 184 - } 185 - return; 186 - } 187 - } 188 - e.preventDefault(); 189 - e.stopPropagation(); 190 - }} 225 + onPaste={async (e) => {}} 191 226 id={elementId.block(props.entityID).text} 192 227 className={`textBlock w-full p-0 border-none outline-none resize-none align-top bg-transparent whitespace-pre-wrap`} 193 228 ref={setMount}
+8 -2
components/TextBlock/schema.ts
··· 1 1 import { Schema } from "prosemirror-model"; 2 2 import { marks } from "prosemirror-schema-basic"; 3 3 4 - export const schema = new Schema({ 4 + let baseSchema = { 5 5 marks: { 6 6 strong: marks.strong, 7 7 em: marks.em, ··· 12 12 content: "inline*", 13 13 group: "block", 14 14 parseDOM: [{ tag: "p" }], 15 - toDOM: () => ["p", 0], 15 + toDOM: () => ["p", 0] as const, 16 16 }, 17 17 text: { 18 18 group: "inline", 19 19 }, 20 20 }, 21 + }; 22 + export const schema = new Schema(baseSchema); 23 + 24 + export const multiBlockSchema = new Schema({ 25 + marks: baseSchema.marks, 26 + nodes: { ...baseSchema.nodes, doc: { content: "block+" } }, 21 27 });