import * as React from "react"; import { DecorationType, BlockType, ContentValueType, BlockMapType, MapPageUrl, MapImageUrl, CustomBlockComponents, BlockValueProp, CustomDecoratorComponents, CustomDecoratorComponentProps } from "./types"; import Asset from "./components/asset"; import Code from "./components/code"; import PageIcon from "./components/page-icon"; import PageHeader from "./components/page-header"; import { classNames, getTextContent, getListNumber } from "./utils"; export const createRenderChildText = ( customDecoratorComponents?: CustomDecoratorComponents ) => (properties: DecorationType[]) => { return properties?.map(([text, decorations], i) => { if (!decorations) { return {text}; } return decorations.reduceRight((element, decorator) => { const renderText = () => { switch (decorator[0]) { case "h": return ( {element} ); case "c": return ( {element} ); case "b": return {element}; case "i": return {element}; case "s": return {element}; case "a": return ( {element} ); default: return {element}; } }; const CustomComponent = customDecoratorComponents?.[decorator[0]]; if (CustomComponent) { const props = (decorator[1] ? { decoratorValue: decorator[1] } : {}) as CustomDecoratorComponentProps; return ( {text} ); } return renderText(); }, <>{text}); }); }; interface Block { block: BlockType; level: number; blockMap: BlockMapType; mapPageUrl: MapPageUrl; mapImageUrl: MapImageUrl; fullPage?: boolean; hideHeader?: boolean; customBlockComponents?: CustomBlockComponents; customDecoratorComponents?: CustomDecoratorComponents; } export const Block: React.FC = props => { const { block, children, level, fullPage, hideHeader, blockMap, mapPageUrl, mapImageUrl, customBlockComponents, customDecoratorComponents } = props; const blockValue = block?.value; const renderComponent = () => { const renderChildText = createRenderChildText(customDecoratorComponents); switch (blockValue?.type) { case "page": if (level === 0) { if (fullPage) { if (!blockValue.properties) { return null; } const { page_icon, page_cover, page_cover_position, page_full_width, page_small_text } = blockValue.format || {}; const coverPosition = (1 - (page_cover_position || 0.5)) * 100; return (
{!hideHeader && ( )} {page_cover && ( {getTextContent(blockValue.properties.title)} )}
{page_icon && ( )}
{renderChildText(blockValue.properties.title)}
{children}
); } else { return
{children}
; } } else { if (!blockValue.properties) return null; return ( {blockValue.format && (
)}
{renderChildText(blockValue.properties.title)}
); } case "header": if (!blockValue.properties) return null; return (

{renderChildText(blockValue.properties.title)}

); case "sub_header": if (!blockValue.properties) return null; return (

{renderChildText(blockValue.properties.title)}

); case "sub_sub_header": if (!blockValue.properties) return null; return (

{renderChildText(blockValue.properties.title)}

); case "divider": return
; case "text": if (!blockValue.properties) { return
 
; } const blockColor = blockValue.format?.block_color; return (

{renderChildText(blockValue.properties.title)}

); case "bulleted_list": case "numbered_list": const wrapList = (content: React.ReactNode, start?: number) => blockValue.type === "bulleted_list" ? ( ) : (
    {content}
); let output: JSX.Element | null = null; if (blockValue.content) { output = ( <> {blockValue.properties && (
  • {renderChildText(blockValue.properties.title)}
  • )} {wrapList(children)} ); } else { output = blockValue.properties ? (
  • {renderChildText(blockValue.properties.title)}
  • ) : null; } const isTopLevel = block.value.type !== blockMap[block.value.parent_id].value.type; const start = getListNumber(blockValue.id, blockMap); return isTopLevel ? wrapList(output, start) : output; case "image": case "embed": case "figma": case "video": const value = block.value as ContentValueType; return (
    {value.properties.caption && (
    {renderChildText(value.properties.caption)}
    )}
    ); case "code": { if (blockValue.properties.title) { const content = blockValue.properties.title[0][0]; const language = blockValue.properties.language[0][0]; return ( ); } break; } case "column_list": return
    {children}
    ; case "column": const spacerWith = 46; const ratio = blockValue.format.column_ratio; const columns = Number((1 / ratio).toFixed(0)); const spacerTotalWith = (columns - 1) * spacerWith; const width = `calc((100% - ${spacerTotalWith}px) * ${ratio})`; return ( <>
    {children}
    ); case "quote": if (!blockValue.properties) return null; return (
    {renderChildText(blockValue.properties.title)}
    ); case "collection_view": if (!block) return null; const collectionView = block?.collection?.types[0]; return (

    {renderChildText(block.collection?.title!)}

    {collectionView?.type === "table" && (
    {collectionView.format?.table_properties ?.filter(p => p.visible) .map((gp, index) => ( ))} {block?.collection?.data.map((row, index) => ( {collectionView.format?.table_properties ?.filter(p => p.visible) .map((gp, index) => ( ))} ))}
    {block.collection?.schema[gp.property]?.name}
    { renderChildText( row[ block.collection?.schema[gp.property]?.name! ] )! }
    )} {collectionView?.type === "gallery" && (
    {block.collection?.data.map((row, i) => (
    {collectionView.format?.gallery_properties ?.filter(p => p.visible) .map((gp, idx) => (

    {getTextContent( row[block.collection?.schema[gp.property].name!] )}

    ))}
    ))}
    )}
    ); case "callout": return (
    {renderChildText(blockValue.properties.title)}
    ); case "bookmark": const link = blockValue.properties.link; const title = blockValue.properties.title ?? link; const description = blockValue.properties.description; const block_color = blockValue.format?.block_color; const bookmark_icon = blockValue.format?.bookmark_icon; const bookmark_cover = blockValue.format?.bookmark_cover; return (
    {renderChildText(title)}
    {description && (
    {renderChildText(description)}
    )}
    {bookmark_icon && ( {getTextContent(title)} )}
    {renderChildText(link)}
    {bookmark_cover && (
    {getTextContent(title)}
    )}
    ); case "toggle": return (
    {renderChildText(blockValue.properties.title)}
    {children}
    ); default: if (process.env.NODE_ENV !== "production") { console.log("Unsupported type " + block?.value?.type); } return
    ; } return null; }; // render a custom component first if passed. if ( customBlockComponents && customBlockComponents[blockValue?.type] && // Do not use custom component for base page block level !== 0 ) { const CustomComponent = customBlockComponents[blockValue?.type]!; return ( } level={level} > {children} ); } return renderComponent(); };