diff --git a/components/Blocks/Block.tsx b/components/Blocks/Block.tsx index 8fa2a138..db77e514 100644 --- a/components/Blocks/Block.tsx +++ b/components/Blocks/Block.tsx @@ -104,6 +104,7 @@ export const Block = memo(function Block( ? "pb-0" : "pb-2" } + ${props.type === "blockquote" && props.previousBlock?.type === "blockquote" ? "-mt-3" : ""} ${ !props.previousBlock ? props.type === "heading" || props.type === "text" diff --git a/components/Blocks/BlockCommands.tsx b/components/Blocks/BlockCommands.tsx index 078c66d9..f612ec55 100644 --- a/components/Blocks/BlockCommands.tsx +++ b/components/Blocks/BlockCommands.tsx @@ -31,7 +31,6 @@ import { BlockRSVPSmall } from "components/Icons/BlockRSVPSmall"; import { ListUnorderedSmall } from "components/Toolbar/ListToolbar"; import { BlockMathSmall } from "components/Icons/BlockMathSmall"; import { BlockCodeSmall } from "components/Icons/BlockCodeSmall"; -import { QuoteTiny } from "components/Icons/QuoteTiny"; import { QuoteSmall } from "components/Icons/QuoteSmall"; type Props = { @@ -160,16 +159,16 @@ export const blockCommands: Command[] = [ clearCommandSearchText(entity); }, }, - // { - // name: "Block Quote", - // icon: , - // type: "text", - // onSelect: async (rep, props, um) => { - // if (props.entityID) clearCommandSearchText(props.entityID); - // let entity = await createBlockWithType(rep, props, "blockquote"); - // clearCommandSearchText(entity); - // }, - // }, + { + name: "Block Quote", + icon: , + type: "text", + onSelect: async (rep, props, um) => { + if (props.entityID) clearCommandSearchText(props.entityID); + let entity = await createBlockWithType(rep, props, "blockquote"); + clearCommandSearchText(entity); + }, + }, { name: "Image", diff --git a/components/Blocks/TextBlock/RenderYJSFragment.tsx b/components/Blocks/TextBlock/RenderYJSFragment.tsx index 0ad90ed8..f4c6b69d 100644 --- a/components/Blocks/TextBlock/RenderYJSFragment.tsx +++ b/components/Blocks/TextBlock/RenderYJSFragment.tsx @@ -9,7 +9,7 @@ export function RenderYJSFragment({ attrs, }: { node: XmlElement | XmlText | XmlHook; - wrapper?: "h1" | "h2" | "h3" | null; + wrapper?: "h1" | "h2" | "h3" | null | "blockquote"; attrs?: { [k: string]: any }; }) { if (node.constructor === XmlElement) { @@ -63,13 +63,16 @@ export function RenderYJSFragment({ } const BlockWrapper = (props: { - wrapper?: "h1" | "h2" | "h3" | null; + wrapper?: "h1" | "h2" | "h3" | null | "blockquote"; children: React.ReactNode; attrs?: { [k: string]: any }; }) => { if (props.wrapper === null) return <>{props.children}; if (!props.wrapper) return

{props.children}

; switch (props.wrapper) { + case "blockquote": + return
{props.children}
; + case "h1": return

{props.children}

; case "h2": diff --git a/components/Blocks/TextBlock/index.tsx b/components/Blocks/TextBlock/index.tsx index 8441e378..3e26cb04 100644 --- a/components/Blocks/TextBlock/index.tsx +++ b/components/Blocks/TextBlock/index.tsx @@ -218,14 +218,9 @@ export function BaseTextBlock(props: BlockProps & { className?: string }) { let handlePaste = useHandlePaste(props.entityID, propsRef); useLayoutEffect(() => { if (!mountRef.current) return; - let km = TextBlockKeymap( - propsRef, - repRef, - rep.undoManager, - props.type === "blockquote", - ); + let km = TextBlockKeymap(propsRef, repRef, rep.undoManager); let editor = EditorState.create({ - schema: props.type === "blockquote" ? multiBlockSchema : schema, + schema: schema, plugins: [ ySyncPlugin(value), keymap(km), @@ -342,7 +337,7 @@ export function BaseTextBlock(props: BlockProps & { className?: string }) { }, })); }; - }, [props.entityID, props.parent, value, handlePaste, rep, props.type]); + }, [props.entityID, props.parent, value, handlePaste, rep]); return ( <> diff --git a/components/Blocks/TextBlock/inputRules.ts b/components/Blocks/TextBlock/inputRules.ts index a4748259..eb33bafd 100644 --- a/components/Blocks/TextBlock/inputRules.ts +++ b/components/Blocks/TextBlock/inputRules.ts @@ -152,17 +152,17 @@ export const inputrules = ( return tr; }), - // //Blockquote - // new InputRule(/^([>]{1})\s$/, (state, match) => { - // let tr = state.tr; - // tr.delete(0, 2); - // repRef.current?.mutate.assertFact({ - // entity: propsRef.current.entityID, - // attribute: "block/type", - // data: { type: "block-type-union", value: "blockquote" }, - // }); - // return tr; - // }), + //Blockquote + new InputRule(/^([>]{1})\s$/, (state, match) => { + let tr = state.tr; + tr.delete(0, 2); + repRef.current?.mutate.assertFact({ + entity: propsRef.current.entityID, + attribute: "block/type", + data: { type: "block-type-union", value: "blockquote" }, + }); + return tr; + }), //Header new InputRule(/^([#]{1,3})\s$/, (state, match) => { diff --git a/components/Blocks/TextBlock/useHandlePaste.ts b/components/Blocks/TextBlock/useHandlePaste.ts index c1161dea..59589496 100644 --- a/components/Blocks/TextBlock/useHandlePaste.ts +++ b/components/Blocks/TextBlock/useHandlePaste.ts @@ -254,8 +254,7 @@ const createBlockFromHTML = ( default: type = null; } - let content = - type === "blockquote" ? multilineParser.parse(child) : parser.parse(child); + let content = parser.parse(child); if (!type) return; let entityID: string; @@ -498,11 +497,7 @@ const createBlockFromHTML = ( block.editor.selection.to !== undefined ) tr.delete(block.editor.selection.from, block.editor.selection.to); - if (type === "blockquote") { - tr.replaceWith(0, tr.doc.content.size, content.content); - } else { - tr.replaceSelectionWith(content); - } + tr.replaceSelectionWith(content); let newState = block.editor.apply(tr); setEditorState(entityID, { editor: newState, diff --git a/src/utils/focusBlock.ts b/src/utils/focusBlock.ts index 931dfa4b..99606df8 100644 --- a/src/utils/focusBlock.ts +++ b/src/utils/focusBlock.ts @@ -104,14 +104,9 @@ export function focusBlock( } } - if (block.type === "blockquote" && position.type === "start") { - let sel = NodeSelection.create(tr.doc, 0); - nextBlock.view.dispatch(tr.setSelection(sel)); - } else { - nextBlock.view.dispatch( - tr.setSelection(TextSelection.create(tr.doc, pos?.pos || 1)), - ); - } + nextBlock.view.dispatch( + tr.setSelection(TextSelection.create(tr.doc, pos?.pos || 1)), + ); nextBlock.view.focus(); } diff --git a/src/utils/getBlocksAsHTML.tsx b/src/utils/getBlocksAsHTML.tsx index cb89a099..8e22afd8 100644 --- a/src/utils/getBlocksAsHTML.tsx +++ b/src/utils/getBlocksAsHTML.tsx @@ -74,7 +74,7 @@ async function renderBlock( tx: ReadTransaction, ignoreWrapper?: boolean, ) { - let wrapper: undefined | "h1" | "h2" | "h3"; + let wrapper: undefined | "h1" | "h2" | "h3" | "blockquote"; let [alignment] = await scanIndex(tx).eav(b.value, "block/text-alignment"); if (b.type === "horizontal-rule") { return "
"; @@ -125,6 +125,9 @@ async function renderBlock( , ); } + if (b.type === "blockquote") { + wrapper = "blockquote"; + } if (b.type === "heading") { let headingLevel = (await scanIndex(tx).eav(b.value, "block/heading-level"))[0]?.data @@ -162,32 +165,8 @@ async function renderBlock( , ); } - if (b.type === "blockquote") { - let value = (await scanIndex(tx).eav(b.value, "block/text"))[0]; - if (!value) return "
"; - let doc = new Y.Doc(); - const update = base64.toByteArray(value.data.value); - Y.applyUpdate(doc, update); - let nodes = doc.getXmlElement("prosemirror").toArray(); - //Have to handle this specially because it's a multi-line block - return `
${nodes - .map((node) => { - if (node.constructor === Y.XmlElement) { - let children = node.toArray(); - if (children.length === 0) return "

"; - return renderToStaticMarkup( - , - ); - } - }) - .join("\n")}
`; - } let value = (await scanIndex(tx).eav(b.value, "block/text"))[0]; + console.log("getBlockasHTML", value); if (!value) return ignoreWrapper ? "" : `<${wrapper || "p"}>`; let doc = new Y.Doc(); diff --git a/src/utils/isTextBlock.ts b/src/utils/isTextBlock.ts index f1f95c73..0020cc94 100644 --- a/src/utils/isTextBlock.ts +++ b/src/utils/isTextBlock.ts @@ -5,4 +5,5 @@ export const isTextBlock: { } = { text: true, heading: true, + blockquote: true, };