import { Block, BlockConfig, blockHasType, defaultToggledState, UnreachableCaseError, } from "@blocknote/core"; import { ReactNode, useReducer } from "react"; import { useEditorState } from "../../hooks/useEditorState.js"; import { ReactCustomBlockRenderProps } from "../../schema/ReactBlockSpec.js"; const showChildrenReducer = ( showChildren: boolean, action: | { type: "toggled"; } | { type: "childAdded"; } | { type: "lastChildRemoved"; }, ) => { if (action.type === "toggled") { return !showChildren; } if (action.type === "childAdded") { return true; } if (action.type === "lastChildRemoved") { return false; } throw new UnreachableCaseError(action); }; export const ToggleWrapper = ( props: Omit< ReactCustomBlockRenderProps>, "contentRef" > & { children: ReactNode; toggledState?: { set: (block: Block, isToggled: boolean) => void; get: (block: Block) => boolean; }; }, ) => { const { block, editor, children, toggledState } = props; const [showChildren, dispatch] = useReducer( showChildrenReducer, (toggledState || defaultToggledState).get(block), ); const handleToggle = (block: Block) => { (toggledState || defaultToggledState).set( editor.getBlock(block)!, !showChildren, ); dispatch({ type: "toggled", }); }; const handleChildAdded = (block: Block) => { (toggledState || defaultToggledState).set(block, true); dispatch({ type: "childAdded", }); }; const handleLastChildRemoved = (block: Block) => { (toggledState || defaultToggledState).set(block, false); dispatch({ type: "lastChildRemoved", }); }; const childCount = useEditorState({ editor, selector: ({ editor }) => { if ( !blockHasType(block, editor, block.type, { isToggleable: "boolean" }) && !block.props.isToggleable ) { return 0; } const newBlock = editor.getBlock(block)!; const newChildCount = newBlock.children.length || 0; if (newChildCount > childCount) { // If a child block is added while children are hidden, show children. if (!showChildren) { handleChildAdded(newBlock); } } else if (newChildCount === 0 && newChildCount < childCount) { // If the last child block is removed while children are shown, hide // children. if (showChildren) { handleLastChildRemoved(newBlock); } } return newChildCount; }, }); if ("isToggleable" in block.props && !block.props.isToggleable) { return children; } return (
{children}
{editor.isEditable && showChildren && childCount === 0 && ( )}
); };