import { jsx as _jsx } from "react/jsx-runtime";
import { applyNonSelectableBlockFix, camelToDataKebab, createInternalBlockSpec, createStronglyTypedTiptapNode, getBlockFromPos, getParseRules, inheritedProps, mergeCSSClasses, propsToAttributes, wrapInBlockStructure, } from "@endexai/blocknote-core";
import { NodeViewWrapper, ReactNodeViewRenderer, useReactNodeView, } from "@tiptap/react";
import { renderToDOMSpec } from "./@util/ReactRenderUtil.js";
// Function that wraps the React component returned from 'blockConfig.render' in
// a `NodeViewWrapper` which also acts as a `blockContent` div. It contains the
// block type and props as HTML attributes.
export function BlockContentWrapper(props) {
    return (
    // Creates `blockContent` element
    _jsx(NodeViewWrapper
    // Adds custom HTML attributes
    , { ...Object.fromEntries(Object.entries(props.domAttributes || {}).filter(([key]) => key !== "class")), 
        // Sets blockContent class
        className: mergeCSSClasses("bn-block-content", props.domAttributes?.class || ""), "data-content-type": props.blockType, ...Object.fromEntries(Object.entries(props.blockProps)
            .filter(([prop, value]) => !inheritedProps.includes(prop) &&
            value !== props.propSchema[prop].default)
            .map(([prop, value]) => {
            return [camelToDataKebab(prop), value];
        })), "data-file-block": props.isFileBlock === true || undefined, children: props.children }));
}
// A function to create custom block for API consumers
// we want to hide the tiptap node from API consumers and provide a simpler API surface instead
export function createReactBlockSpec(blockConfig, blockImplementation) {
    const node = createStronglyTypedTiptapNode({
        name: blockConfig.type,
        content: (blockConfig.content === "inline"
            ? "inline*"
            : ""),
        group: "blockContent",
        selectable: blockConfig.isSelectable ?? true,
        addAttributes() {
            return propsToAttributes(blockConfig.propSchema);
        },
        parseHTML() {
            return getParseRules(blockConfig, blockImplementation.parse);
        },
        renderHTML({ HTMLAttributes }) {
            // renderHTML is used for copy/pasting content from the editor back into
            // the editor, so we need to make sure the `blockContent` element is
            // structured correctly as this is what's used for parsing blocks. We
            // just render a placeholder div inside as the `blockContent` element
            // already has all the information needed for proper parsing.
            const div = document.createElement("div");
            return wrapInBlockStructure({
                dom: div,
                contentDOM: blockConfig.content === "inline" ? div : undefined,
            }, blockConfig.type, {}, blockConfig.propSchema, blockConfig.isFileBlock, HTMLAttributes);
        },
        addNodeView() {
            return (props) => {
                const nodeView = ReactNodeViewRenderer((props) => {
                    // Gets the BlockNote editor instance
                    const editor = this.options.editor;
                    // Gets the block
                    const block = getBlockFromPos(props.getPos, editor, this.editor, blockConfig.type);
                    // Gets the custom HTML attributes for `blockContent` nodes
                    const blockContentDOMAttributes = this.options.domAttributes?.blockContent || {};
                    const ref = useReactNodeView().nodeViewContentRef;
                    if (!ref) {
                        throw new Error("nodeViewContentRef is not set");
                    }
                    const BlockContent = blockImplementation.render;
                    return (_jsx(BlockContentWrapper, { blockType: block.type, blockProps: block.props, propSchema: blockConfig.propSchema, isFileBlock: blockConfig.isFileBlock, domAttributes: blockContentDOMAttributes, children: _jsx(BlockContent, { block: block, editor: editor, contentRef: ref }) }));
                }, {
                    className: "bn-react-node-view-renderer",
                })(props);
                if (blockConfig.isSelectable === false) {
                    applyNonSelectableBlockFix(nodeView, this.editor);
                }
                return nodeView;
            };
        },
    });
    return createInternalBlockSpec(blockConfig, {
        node: node,
        toInternalHTML: (block, editor) => {
            const blockContentDOMAttributes = node.options.domAttributes?.blockContent || {};
            const BlockContent = blockImplementation.render;
            const output = renderToDOMSpec((refCB) => (_jsx(BlockContentWrapper, { blockType: block.type, blockProps: block.props, propSchema: blockConfig.propSchema, domAttributes: blockContentDOMAttributes, children: _jsx(BlockContent, { block: block, editor: editor, contentRef: refCB }) })), editor);
            output.contentDOM?.setAttribute("data-editable", "");
            return output;
        },
        toExternalHTML: (block, editor) => {
            const blockContentDOMAttributes = node.options.domAttributes?.blockContent || {};
            const BlockContent = blockImplementation.toExternalHTML || blockImplementation.render;
            const output = renderToDOMSpec((refCB) => {
                return (_jsx(BlockContentWrapper, { blockType: block.type, blockProps: block.props, propSchema: blockConfig.propSchema, domAttributes: blockContentDOMAttributes, children: _jsx(BlockContent, { block: block, editor: editor, contentRef: refCB }) }));
            }, editor);
            output.contentDOM?.setAttribute("data-editable", "");
            return output;
        },
    });
}
//# sourceMappingURL=ReactBlockSpec.js.map