splitbee / react-notion

A fast React renderer for Notion pages
https://react-notion.com
MIT License
2.86k stars 151 forks source link

Pass currentBlock to CustomBlockComponent #53

Closed pyk closed 3 years ago

pyk commented 3 years ago

Previously, I can't create a custom block component for image because I can't access the parent block of the image.

<NotionRenderer
    blockMap={notionBlocks ?? {}}
    customBlockComponents={{
        image: ({ blockValue }) => {
            // Image cannot be loaded because it need the parent and file id
            const src = defaultMapImageUrl(
                blockValue.properties.source[0][0]
            );
            const caption = blockValue.properties.caption?.[0][0];
            return (
                <figure>
                    <div>
                        <img src={src} alt={caption} />
                    </div>
                </figure>
            );
        },
    }}
/>

With this changes, now I can do the following:

<NotionRenderer
    blockMap={notionBlocks ?? {}}
    customBlockComponents={{
        // NOTE: currentBlock now passed to this custom component
        image: ({ block, blockValue }) => {
            const src = defaultMapImageUrl(
                blockValue.properties.source[0][0],
                block
            );
            const caption = blockValue.properties.caption?.[0][0];
            return (
                <figure>
                    <div>
                        <img src={src} alt={caption} />
                    </div>
                </figure>
            );
        },
    }}
/>

Now the image can be loaded correctly in CustomBlockComponent.

pyk commented 3 years ago

Update:

turns out I can pass block using blockValue:

/**
 * React Component to render Notion's image block
 */
export const NotionImage: FunctionComponent<
    CustomBlockComponentProps<"image">
> = ({ blockValue }) => { 
    // NOTE: like the following
    const block: BlockType = {
        // @ts-ignore
        value: {
            type: "image",
            ...blockValue,
        },
    };
    const notionImageUrl = defaultMapImageUrl(
        blockValue.properties.source[0][0],
        block
    );
   ...
};

So instead of passing block to CustomComponent, I changes the PR to pass blockMap instead.

It is useful for implementing custom numbered_list component.

For example:

/**
 * React Component to render Notion's numbered list block
 */
export const NotionNumberedList: FunctionComponent<
    CustomBlockComponentProps<"numbered_list">
> = ({ blockMap, blockValue, children }) => {
    // Get total number of elements in order to create custom classes for the latest
    // <ol> element
    const totalListElements = getTotalListMember(blockValue.id, blockMap);

    const wrapList = (content: React.ReactNode, start?: number) => {
        return (
            // Increase the margin for last element
            <ol
                start={start}
                className={`list-decimal text-fg-200 text-xl tracking-wide leading-normal list-outside pl-8 ${
                    start == totalListElements ? "mb-8" : "mb-4"
                }`}
            >
                {content}
            </ol>
        );
    };

    let output: JSX.Element | null = null;

    if (blockValue.content) {
        output = (
            <>
                {blockValue.properties && (
                    <li className={styles.listMarker}>
                        {notionRenderChildText()(blockValue.properties.title)}
                    </li>
                )}
                {wrapList(children)}
            </>
        );
    } else {
        output = blockValue.properties ? (
            <li className={styles.listMarker}>
                {notionRenderChildText()(blockValue.properties.title)}
            </li>
        ) : null;
    }

    const parentBlockType = blockMap[blockValue.parent_id].value.type;
    const isTopLevel = parentBlockType == "numbered_list" ? false : true;
    const start = getListNumber(blockValue.id, blockMap);

    return isTopLevel ? wrapList(output, start) : output;
};
tobiaslins commented 3 years ago

Good addition, thanks!! :)