From 8c9dcc33ef5724c2283c02a822888564be3325cd Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Wed, 10 Sep 2025 09:59:22 -0600 Subject: [PATCH] Update toggle node --- .../ui/report/elements/ToggleNode.tsx | 69 ++++++++++++------- 1 file changed, 46 insertions(+), 23 deletions(-) diff --git a/apps/web/src/components/ui/report/elements/ToggleNode.tsx b/apps/web/src/components/ui/report/elements/ToggleNode.tsx index 34c126d26..2696507e3 100644 --- a/apps/web/src/components/ui/report/elements/ToggleNode.tsx +++ b/apps/web/src/components/ui/report/elements/ToggleNode.tsx @@ -1,4 +1,6 @@ -import { useToggleButton, useToggleButtonState } from '@platejs/toggle/react'; +import { indent } from '@platejs/indent'; +import { buildToggleIndex, useToggleButton, useToggleButtonState } from '@platejs/toggle/react'; +import { KEYS } from 'platejs'; import type { PlateElementProps } from 'platejs/react'; import { PlateElement, useEditorRef, useElement, useElementSelector } from 'platejs/react'; import * as React from 'react'; @@ -11,33 +13,37 @@ export function ToggleElement(props: PlateElementProps) { const state = useToggleButtonState(element.id as string); const { buttonProps, open } = useToggleButton(state); const editor = useEditorRef(); - const prevOpenRef = React.useRef(open); + + const toggleParentHasContent = element.children.some((child) => child.text); + const hasContent = useToggleHasContent(element.id as string); React.useEffect(() => { - const wasOpen = prevOpenRef.current; - prevOpenRef.current = open; + if (open && !hasContent) { + // Find the path of the current toggle element + const togglePath = editor.api.findPath(element); - if (!wasOpen && open) { - type Child = { type?: string; text?: string }; - const children = (element.children ?? []) as Child[]; + if (togglePath) { + // Insert a paragraph after the toggle element + const paragraphPath = [togglePath[0] + 1]; + const paragraphNode = { + type: KEYS.p, + children: [{ text: '' }], + }; - const hasBlockChildren = children.some((c) => typeof c.type === 'string'); - const hasTextContent = children.some((c) => c.text); - - if (!hasBlockChildren && !hasTextContent) { - const path = editor.api.findPath(element); - if (path) { - const newParagraph = editor.api.create.block(); - editor.tf.insertNodes(newParagraph, { - at: path.concat(children.length), - select: true, - }); - } + editor.tf.withoutNormalizing(() => { + editor.tf.insertNodes(paragraphNode, { at: paragraphPath }); + // Select the paragraph after insertion + const endPoint = { + path: [...paragraphPath, 0], + offset: 0, + }; + editor.tf.select(endPoint); + // Apply indentation to make it part of the toggle's content + indent(editor); + }); } } - }, [editor, element, open]); - - const hasContent = element.children.some((child) => child.text); + }, [open, hasContent, editor, element]); return ( @@ -53,7 +59,7 @@ export function ToggleElement(props: PlateElementProps) { } /> - {!hasContent && ( + {!toggleParentHasContent && ( ); } + +function useToggleHasContent(toggleId: string): boolean { + const editor = useEditorRef(); + + return React.useMemo(() => { + const toggleIndex = buildToggleIndex(editor.children); + + // Check if any elements are enclosed within this toggle + for (const [_elementId, enclosingToggleIds] of toggleIndex.entries()) { + if (enclosingToggleIds.includes(toggleId)) { + return true; + } + } + + return false; + }, [editor.children, toggleId]); +}