I think perhaps my approach for creating this kind of component, might be completely incorrect.
I want to create a sort of tab which will open and close when the arrow is clicked and keep it editable at the same time. I want to have the component not delete itself unless there is no content inside.
What happens with this though, is the component when editing just deletes.
Parent Component
import { useTheme } from "next-themes";
import { useEdgeStore } from "@/lib/edgestore";
import { BlockNoteEditor, BlockNoteSchema, defaultBlockSpecs, filterSuggestionItems, insertOrUpdateBlock } from "@blocknote/core";
import { BlockNoteView, SuggestionMenuController, getDefaultReactSlashMenuItems, useCreateBlockNote,} from "@blocknote/react";
import "@blocknote/react/style.css";
import { RiArrowRightFill } from 'react-icons/ri';
import { ParagraphToggle } from './paragraph-toggle/paragraph-toggle';
const schema = BlockNoteSchema.create({
blockSpecs: {
// Adds all default blocks.
...defaultBlockSpecs,
toggle: ParagraphToggle,
},
});
interface EditorProps {
onChange: (value: string) => void;
initialContent?: string;
editable?: boolean;
}
const insertToggle = (editor: typeof schema.BlockNoteEditor) => ({
title: 'Toggle-Paragraph',
onItemClick: () => {
insertOrUpdateBlock(editor, {
type: 'toggle',
props: {
title: 'Toggle Paragraph Title',
isOpen: false,
},
});
},
aliases: ['toggle', 'dropdown'],
group: 'Headings',
icon: <RiArrowRightFill />,
});
const Editor = ({ onChange, initialContent, editable }: EditorProps) => {
const { resolvedTheme } = useTheme();
const { edgestore } = useEdgeStore();
const handleUpload = async (file: File) => {
const response = await edgestore.publicFiles.upload({ file });
return response.url;
};
const editor = useCreateBlockNote({
schema,
initialContent: initialContent ? JSON.parse(initialContent) : undefined,
uploadFile: handleUpload
});
return(
<BlockNoteView editor={editor} slashMenu={false} theme={resolvedTheme === "dark" ? "dark" : "light"}>
{/* Replaces the default Slash Menu. */}
<SuggestionMenuController
triggerCharacter={'/'}
getItems={async (query) =>
filterSuggestionItems(
[
...getDefaultReactSlashMenuItems(editor),
/**Add here new components for menu */
insertToggle(editor),
],
query
)
}
/>
</BlockNoteView>
);
};
export default Editor;
Child component:
import React, { useState, useRef, useEffect } from 'react';
import { defaultProps } from '@blocknote/core';
import { createReactBlockSpec } from '@blocknote/react';
export const ParagraphToggle = createReactBlockSpec(
{
type: 'toggle',
propSchema: {
textAlignment: defaultProps.textAlignment,
textColor: defaultProps.textColor,
isOpen: {
default: false,
values: [true, false],
},
title: {
default: 'Toggle Title',
values: [],
},
},
content: 'inline',
},
{
render: (props) => {
return (
<details contentEditable="true">
<pre></pre>
<summary>Edit Summary</summary>
<p>Edit Details</p>
</details>
);
},
}
);
export default ParagraphToggle;
Here is an image of what I am trying to acheive.