"use client"; import { Node, mergeAttributes } from "@tiptap/react"; import { NodeViewContent, NodeViewWrapper, ReactNodeViewRenderer, type NodeViewProps, } from "@tiptap/react"; import { ChevronDown, ChevronRight } from "lucide-react"; import * as React from "react"; import { cn } from "@/lib/utils"; function ToggleView(props: NodeViewProps) { const { node, updateAttributes, selected } = props; const open = Boolean(node.attrs.open); const title = typeof node.attrs.title === "string" ? node.attrs.title : ""; const onTitleChange = (e: React.ChangeEvent) => { updateAttributes({ title: e.target.value }); }; return ( ) => { updateAttributes({ open: e.currentTarget.open }); }} > {open ? ( ) : ( )} e.stopPropagation()} />
); } export const Toggle = Node.create({ name: "toggle", group: "block", content: "block+", defining: true, addAttributes() { return { open: { default: true, parseHTML: (el) => el instanceof HTMLElement && el.hasAttribute("open"), renderHTML: (attrs) => (attrs.open ? { open: "" } : {}), }, title: { default: "", parseHTML: (el) => { if (!(el instanceof HTMLElement)) return ""; const s = el.querySelector(":scope > summary"); return s?.textContent?.trim() ?? ""; }, renderHTML: (attrs) => ({}), }, }; }, parseHTML() { return [ { tag: 'details[data-type="toggle"]', contentElement: 'div[data-toggle-body="true"]', }, ]; }, renderHTML({ node, HTMLAttributes }) { return [ "details", mergeAttributes(HTMLAttributes, { "data-type": "toggle", ...(node.attrs.open ? { open: "" } : {}), }), [ "summary", { class: "toggle-summary" }, node.attrs.title ? String(node.attrs.title) : "\u00a0", ], ["div", { "data-toggle-body": "true", class: "toggle-body" }, 0], ]; }, addNodeView() { return ReactNodeViewRenderer(ToggleView); }, addKeyboardShortcuts() { return { "Mod-Shift-Alt-t": () => this.editor.commands.insertContent({ type: this.name, attrs: { open: true, title: "" }, content: [{ type: "paragraph" }], }), }; }, });