From 3121e31bc03cb2f38c00fa5ae33eba3bb325b6dc Mon Sep 17 00:00:00 2001 From: Oleksii Surnin Date: Sun, 29 Oct 2023 20:33:07 +0100 Subject: [PATCH 1/5] Devided block into block-components, added custom components feature --- examples/minimal/components/NotionPage.tsx | 2 +- .../src/block-components/alias.tsx | 31 + .../src/block-components/bookmark.tsx | 94 +++ .../src/block-components/callout.tsx | 30 + .../src/block-components/column-list.tsx | 13 + .../src/block-components/column.tsx | 34 + .../src/block-components/divider.tsx | 12 + .../src/block-components/drive.tsx | 24 + .../src/block-components/header.tsx | 117 +++ .../src/block-components/list.tsx | 57 ++ .../src/block-components/page.tsx | 264 +++++++ .../src/block-components/quote.tsx | 31 + .../block-components/table-of-contents.tsx | 53 ++ .../src/block-components/table-row.tsx | 56 ++ .../src/block-components/table.tsx | 17 + .../src/block-components/text.tsx | 34 + .../src/block-components/to-do.tsx | 40 + .../src/block-components/toggle.tsx | 22 + packages/react-notion-x/src/block.tsx | 730 ++---------------- packages/react-notion-x/src/context.tsx | 43 +- packages/react-notion-x/src/types.ts | 19 + 21 files changed, 1057 insertions(+), 666 deletions(-) create mode 100644 packages/react-notion-x/src/block-components/alias.tsx create mode 100644 packages/react-notion-x/src/block-components/bookmark.tsx create mode 100644 packages/react-notion-x/src/block-components/callout.tsx create mode 100644 packages/react-notion-x/src/block-components/column-list.tsx create mode 100644 packages/react-notion-x/src/block-components/column.tsx create mode 100644 packages/react-notion-x/src/block-components/divider.tsx create mode 100644 packages/react-notion-x/src/block-components/drive.tsx create mode 100644 packages/react-notion-x/src/block-components/header.tsx create mode 100644 packages/react-notion-x/src/block-components/list.tsx create mode 100644 packages/react-notion-x/src/block-components/page.tsx create mode 100644 packages/react-notion-x/src/block-components/quote.tsx create mode 100644 packages/react-notion-x/src/block-components/table-of-contents.tsx create mode 100644 packages/react-notion-x/src/block-components/table-row.tsx create mode 100644 packages/react-notion-x/src/block-components/table.tsx create mode 100644 packages/react-notion-x/src/block-components/text.tsx create mode 100644 packages/react-notion-x/src/block-components/to-do.tsx create mode 100644 packages/react-notion-x/src/block-components/toggle.tsx diff --git a/examples/minimal/components/NotionPage.tsx b/examples/minimal/components/NotionPage.tsx index c39f9653..da28f3b1 100644 --- a/examples/minimal/components/NotionPage.tsx +++ b/examples/minimal/components/NotionPage.tsx @@ -1,9 +1,9 @@ import * as React from 'react' import Head from 'next/head' +import { NotionRenderer } from 'areyes-react-notion-x' import { ExtendedRecordMap } from 'notion-types' import { getPageTitle } from 'notion-utils' -import { NotionRenderer } from 'react-notion-x' export const NotionPage = ({ recordMap, diff --git a/packages/react-notion-x/src/block-components/alias.tsx b/packages/react-notion-x/src/block-components/alias.tsx new file mode 100644 index 00000000..3da6b668 --- /dev/null +++ b/packages/react-notion-x/src/block-components/alias.tsx @@ -0,0 +1,31 @@ +import * as React from 'react' + +import { Block } from 'notion-types' + +import { PageTitle } from '../components/page-title' +import { useNotionContext } from '../context' +import { cs } from '../utils' + +export const Alias: React.FC<{ + blockId: string + block: Block +}> = ({ block }) => { + const ctx = useNotionContext() + const { recordMap, components, mapPageUrl } = ctx + + const blockPointerId = block?.format?.alias_pointer?.id + const linkedBlock = recordMap.block[blockPointerId]?.value + if (!linkedBlock) { + console.log('"alias" missing block', blockPointerId) + return null + } + + return ( + + + + ) +} diff --git a/packages/react-notion-x/src/block-components/bookmark.tsx b/packages/react-notion-x/src/block-components/bookmark.tsx new file mode 100644 index 00000000..dbafb574 --- /dev/null +++ b/packages/react-notion-x/src/block-components/bookmark.tsx @@ -0,0 +1,94 @@ +import * as React from 'react' + +import { Block } from 'notion-types' +import { getTextContent } from 'notion-utils' + +import { LazyImage } from '../components/lazy-image' +import { Text } from '../components/text' +import { useNotionContext } from '../context' +import { cs } from '../utils' + +export const Bookmark: React.FC<{ + blockId: string + block: Block +}> = ({ blockId, block }) => { + const ctx = useNotionContext() + const { components, mapImageUrl } = ctx + + if (!block.properties) return null + + const link = block.properties.link + if (!link || !link[0]?.[0]) return null + + let title = getTextContent(block.properties.title) + if (!title) { + title = getTextContent(link) + } + + if (title) { + if (title.startsWith('http')) { + try { + const url = new URL(title) + title = url.hostname + } catch (err) { + // ignore invalid links + } + } + } + + return ( +
+ +
+ {title && ( +
+ +
+ )} + + {block.properties?.description && ( +
+ +
+ )} + +
+ {block.format?.bookmark_icon && ( +
+ +
+ )} + +
+ +
+
+
+ + {block.format?.bookmark_cover && ( +
+ +
+ )} +
+
+ ) +} diff --git a/packages/react-notion-x/src/block-components/callout.tsx b/packages/react-notion-x/src/block-components/callout.tsx new file mode 100644 index 00000000..32a9286f --- /dev/null +++ b/packages/react-notion-x/src/block-components/callout.tsx @@ -0,0 +1,30 @@ +import * as React from 'react' + +import { Block } from 'notion-types' + +import { PageIcon } from '../components/page-icon' +import { Text } from '../components/text' +import { cs } from '../utils' + +export const Callout: React.FC<{ + blockId: string + block: Block + children?: React.ReactNode +}> = ({ blockId, block, children }) => { + return ( +
+ + +
+ + {children} +
+
+ ) +} diff --git a/packages/react-notion-x/src/block-components/column-list.tsx b/packages/react-notion-x/src/block-components/column-list.tsx new file mode 100644 index 00000000..dc4610b0 --- /dev/null +++ b/packages/react-notion-x/src/block-components/column-list.tsx @@ -0,0 +1,13 @@ +import * as React from 'react' + +import { Block } from 'notion-types' + +import { cs } from '../utils' + +export const ColumnList: React.FC<{ + blockId: string + block: Block + children?: React.ReactNode +}> = ({ blockId, children }) => { + return
{children}
+} diff --git a/packages/react-notion-x/src/block-components/column.tsx b/packages/react-notion-x/src/block-components/column.tsx new file mode 100644 index 00000000..04a943b2 --- /dev/null +++ b/packages/react-notion-x/src/block-components/column.tsx @@ -0,0 +1,34 @@ +import * as React from 'react' + +import { Block } from 'notion-types' + +import { useNotionContext } from '../context' +import { cs } from '../utils' + +export const Column: React.FC<{ + blockId: string + block: Block + children?: React.ReactNode +}> = ({ blockId, children, block }) => { + const ctx = useNotionContext() + const { recordMap } = ctx + + // note: notion uses 46px + const spacerWidth = `min(32px, 4vw)` + const ratio = block.format?.column_ratio || 0.5 + const parent = recordMap.block[block.parent_id]?.value + const columns = parent?.content?.length || Math.max(2, Math.ceil(1.0 / ratio)) + + const width = `calc((100% - (${columns - 1} * ${spacerWidth})) * ${ratio})` + const style = { width } + + return ( + <> +
+ {children} +
+ +
+ + ) +} diff --git a/packages/react-notion-x/src/block-components/divider.tsx b/packages/react-notion-x/src/block-components/divider.tsx new file mode 100644 index 00000000..9a0b6d37 --- /dev/null +++ b/packages/react-notion-x/src/block-components/divider.tsx @@ -0,0 +1,12 @@ +import * as React from 'react' + +import { Block } from 'notion-types' + +import { cs } from '../utils' + +export const Divider: React.FC<{ + blockId: string + block: Block +}> = ({ blockId }) => { + return
+} diff --git a/packages/react-notion-x/src/block-components/drive.tsx b/packages/react-notion-x/src/block-components/drive.tsx new file mode 100644 index 00000000..2d446071 --- /dev/null +++ b/packages/react-notion-x/src/block-components/drive.tsx @@ -0,0 +1,24 @@ +import * as React from 'react' + +import * as types from 'notion-types' +import { Block } from 'notion-types' + +import { AssetWrapper } from '../components/asset-wrapper' +import { GoogleDrive } from '../components/google-drive' + +export const Drive: React.FC<{ + blockId: string + block: Block +}> = ({ blockId, block }) => { + const properties = block.format?.drive_properties + if (!properties) { + //check if this drive actually needs to be embeded ex. google sheets. + if (block.format?.display_source) { + return + } + } + + return ( + + ) +} diff --git a/packages/react-notion-x/src/block-components/header.tsx b/packages/react-notion-x/src/block-components/header.tsx new file mode 100644 index 00000000..7d75a39d --- /dev/null +++ b/packages/react-notion-x/src/block-components/header.tsx @@ -0,0 +1,117 @@ +import * as React from 'react' + +import { Block } from 'notion-types' +import { + getBlockParentPage, + getPageTableOfContents, + getTextContent, + uuidToId +} from 'notion-utils' + +import { Text } from '../components/text' +import { useNotionContext } from '../context' +import { LinkIcon } from '../icons/link-icon' +import { cs } from '../utils' + +// TODO: use react state instead of a global for this +const tocIndentLevelCache: { + [blockId: string]: number +} = {} + +export const TextHeader: React.FC<{ + blockId: string + block: Block + children?: React.ReactNode +}> = ({ blockId, block, children }) => { + const ctx = useNotionContext() + const { recordMap } = ctx + + if (!block.properties) return null + + const blockColor = block.format?.block_color + const id = uuidToId(block.id) + const title = getTextContent(block.properties.title) || `Notion Header ${id}` + + // we use a cache here because constructing the ToC is non-trivial + let indentLevel = tocIndentLevelCache[block.id] + let indentLevelClass: string + + if (indentLevel === undefined) { + const page = getBlockParentPage(block, recordMap) + + if (page) { + const toc = getPageTableOfContents(page, recordMap) + const tocItem = toc.find((tocItem) => tocItem.id === block.id) + + if (tocItem) { + indentLevel = tocItem.indentLevel + tocIndentLevelCache[block.id] = indentLevel + } + } + } + + if (indentLevel !== undefined) { + indentLevelClass = `notion-h-indent-${indentLevel}` + } + + const isH1 = block.type === 'header' + const isH2 = block.type === 'sub_header' + const isH3 = block.type === 'sub_sub_header' + + const classNameStr = cs( + isH1 && 'notion-h notion-h1', + isH2 && 'notion-h notion-h2', + isH3 && 'notion-h notion-h3', + blockColor && `notion-${blockColor}`, + indentLevelClass, + blockId + ) + + const innerHeader = ( + +
+ {!block.format?.toggleable && ( + + + + )} + + + + + + ) + let headerBlock = null + + //page title takes the h1 so all header blocks are greater + if (isH1) { + headerBlock = ( +

+ {innerHeader} +

+ ) + } else if (isH2) { + headerBlock = ( +

+ {innerHeader} +

+ ) + } else { + headerBlock = ( +

+ {innerHeader} +

+ ) + } + + if (block.format?.toggleable) { + return ( +
+ {headerBlock} +
{children}
+
+ ) + } else { + return headerBlock + } +} diff --git a/packages/react-notion-x/src/block-components/list.tsx b/packages/react-notion-x/src/block-components/list.tsx new file mode 100644 index 00000000..d2517939 --- /dev/null +++ b/packages/react-notion-x/src/block-components/list.tsx @@ -0,0 +1,57 @@ +import * as React from 'react' + +import { Block } from 'notion-types' + +import { Text } from '../components/text' +import { useNotionContext } from '../context' +import { cs, getListNumber } from '../utils' + +export const List: React.FC<{ + blockId: string + block: Block + children?: React.ReactNode +}> = ({ blockId, block, children }) => { + const ctx = useNotionContext() + const { recordMap } = ctx + + const wrapList = (content: React.ReactNode, start?: number) => + block.type === 'bulleted_list' ? ( +
    + {content} +
+ ) : ( +
    + {content} +
+ ) + + let output: JSX.Element | null = null + + if (block.content) { + output = ( + <> + {block.properties && ( +
  • + +
  • + )} + {wrapList(children)} + + ) + } else { + output = block.properties ? ( +
  • + +
  • + ) : null + } + + const isTopLevel = + block.type !== recordMap.block[block.parent_id]?.value?.type + const start = getListNumber(block.id, recordMap.block) + + return isTopLevel ? wrapList(output, start) : output +} diff --git a/packages/react-notion-x/src/block-components/page.tsx b/packages/react-notion-x/src/block-components/page.tsx new file mode 100644 index 00000000..dcb23208 --- /dev/null +++ b/packages/react-notion-x/src/block-components/page.tsx @@ -0,0 +1,264 @@ +import * as React from 'react' + +import * as types from 'notion-types' +import { + getBlockCollectionId, + getBlockIcon, + getPageTableOfContents, + getTextContent +} from 'notion-utils' + +import { LazyImage } from '../components/lazy-image' +import { PageAside } from '../components/page-aside' +import { PageIcon } from '../components/page-icon' +import { PageTitle } from '../components/page-title' +import { Text } from '../components/text' +import { useNotionContext } from '../context' +import { cs, isUrl } from '../utils' + +const pageCoverStyleCache: Record = {} + +export const Page: React.FC<{ + block: types.Block + blockId: string + level: number + + className?: string + bodyClassName?: string + + header?: React.ReactNode + footer?: React.ReactNode + pageHeader?: React.ReactNode + pageFooter?: React.ReactNode + pageTitle?: React.ReactNode + pageAside?: React.ReactNode + pageCover?: React.ReactNode + + hideBlockId?: boolean + disableHeader?: boolean + + children?: React.ReactNode +}> = (props) => { + const { + block, + blockId, + children, + level, + className, + bodyClassName, + header, + footer, + pageHeader, + pageFooter, + pageTitle, + pageAside, + pageCover, + disableHeader + } = props + + const [activeSection, setActiveSection] = React.useState(null) + + const ctx = useNotionContext() + const { + components, + fullPage, + darkMode, + recordMap, + mapPageUrl, + mapImageUrl, + showTableOfContents, + minTableOfContentsItems, + defaultPageIcon, + defaultPageCover, + defaultPageCoverPosition + } = ctx + + if (!block.properties) return null + + if (level === 0) { + const { + page_icon = defaultPageIcon, + page_cover = defaultPageCover, + page_cover_position = defaultPageCoverPosition, + page_full_width, + page_small_text + } = block.format || {} + + if (fullPage) { + const properties = + block.type === 'page' + ? block.properties + : { + title: + recordMap.collection[getBlockCollectionId(block, recordMap)] + ?.value?.name + } + + const coverPosition = (1 - (page_cover_position || 0.5)) * 100 + const pageCoverObjectPosition = `center ${coverPosition}%` + let pageCoverStyle = pageCoverStyleCache[pageCoverObjectPosition] + if (!pageCoverStyle) { + pageCoverStyle = pageCoverStyleCache[pageCoverObjectPosition] = { + objectPosition: pageCoverObjectPosition + } + } + + const pageIcon = getBlockIcon(block, recordMap) ?? defaultPageIcon + const isPageIconUrl = pageIcon && isUrl(pageIcon) + + const toc = getPageTableOfContents(block as types.PageBlock, recordMap) + + const hasToc = + showTableOfContents && toc.length >= minTableOfContentsItems + const hasAside = (hasToc || pageAside) && !page_full_width + const hasPageCover = pageCover || page_cover + + return ( +
    +
    + +
    + {!disableHeader && } + {header} + +
    + {hasPageCover && + (pageCover ? ( + pageCover + ) : ( +
    + +
    + ))} + +
    + {page_icon && ( + + )} + + {pageHeader} + +

    + {pageTitle ?? ( + + )} +

    + + {(block.type === 'collection_view_page' || + (block.type === 'page' && + block.parent_table === 'collection')) && ( + + )} + + {block.type !== 'collection_view_page' && ( +
    +
    + {children} +
    + + {hasAside && ( + + )} +
    + )} + + {pageFooter} +
    + + {footer} +
    +
    +
    + ) + } else { + return ( +
    +
    + + {pageHeader} + + {(block.type === 'collection_view_page' || + (block.type === 'page' && block.parent_table === 'collection')) && ( + + )} + + {block.type !== 'collection_view_page' && children} + + {pageFooter} +
    + ) + } + } else { + const blockColor = block.format?.block_color + + return ( + + + + ) + } +} diff --git a/packages/react-notion-x/src/block-components/quote.tsx b/packages/react-notion-x/src/block-components/quote.tsx new file mode 100644 index 00000000..56ea377a --- /dev/null +++ b/packages/react-notion-x/src/block-components/quote.tsx @@ -0,0 +1,31 @@ +import * as React from 'react' + +import { Block } from 'notion-types' + +import { Text } from '../components/text' +import { cs } from '../utils' + +export const Quote: React.FC<{ + blockId: string + block: Block + children?: React.ReactNode +}> = ({ blockId, block, children }) => { + if (!block.properties) return null + + const blockColor = block.format?.block_color + + return ( +
    +
    + +
    + {children} +
    + ) +} diff --git a/packages/react-notion-x/src/block-components/table-of-contents.tsx b/packages/react-notion-x/src/block-components/table-of-contents.tsx new file mode 100644 index 00000000..62d5ec6d --- /dev/null +++ b/packages/react-notion-x/src/block-components/table-of-contents.tsx @@ -0,0 +1,53 @@ +import * as React from 'react' + +import { Block } from 'notion-types' +import { + getBlockParentPage, + getPageTableOfContents, + uuidToId +} from 'notion-utils' + +import { useNotionContext } from '../context' +import { cs } from '../utils' + +export const TableOfContents: React.FC<{ + blockId: string + block: Block +}> = ({ blockId, block }) => { + const ctx = useNotionContext() + const { recordMap } = ctx + + const page = getBlockParentPage(block, recordMap) + if (!page) return null + + const toc = getPageTableOfContents(page, recordMap) + const blockColor = block.format?.block_color + + return ( +
    + {toc.map((tocItem) => ( + + + {tocItem.text} + + + ))} +
    + ) +} diff --git a/packages/react-notion-x/src/block-components/table-row.tsx b/packages/react-notion-x/src/block-components/table-row.tsx new file mode 100644 index 00000000..9aa6a68e --- /dev/null +++ b/packages/react-notion-x/src/block-components/table-row.tsx @@ -0,0 +1,56 @@ +import * as React from 'react' + +import * as types from 'notion-types' +import { Block } from 'notion-types' + +import { Text } from '../components/text' +import { useNotionContext } from '../context' +import { cs } from '../utils' + +export const TableRow: React.FC<{ + blockId: string + block: Block +}> = ({ blockId, block }) => { + const ctx = useNotionContext() + const { recordMap } = ctx + + const tableBlock = recordMap.block[block.parent_id]?.value as types.TableBlock + const order = tableBlock.format?.table_block_column_order + const formatMap = tableBlock.format?.table_block_column_format + const backgroundColor = block.format?.block_color + + if (!tableBlock || !order) { + return null + } + + return ( + + {order.map((column) => { + const color = formatMap?.[column]?.color + + return ( + +
    + +
    + + ) + })} + + ) +} diff --git a/packages/react-notion-x/src/block-components/table.tsx b/packages/react-notion-x/src/block-components/table.tsx new file mode 100644 index 00000000..c5ba6b96 --- /dev/null +++ b/packages/react-notion-x/src/block-components/table.tsx @@ -0,0 +1,17 @@ +import * as React from 'react' + +import { Block } from 'notion-types' + +import { cs } from '../utils' + +export const Table: React.FC<{ + blockId: string + block: Block + children: React.ReactNode +}> = ({ blockId, children }) => { + return ( + + {children} +
    + ) +} diff --git a/packages/react-notion-x/src/block-components/text.tsx b/packages/react-notion-x/src/block-components/text.tsx new file mode 100644 index 00000000..3cc4641a --- /dev/null +++ b/packages/react-notion-x/src/block-components/text.tsx @@ -0,0 +1,34 @@ +import * as React from 'react' + +import { Block } from 'notion-types' + +import { Text } from '../components/text' +import { cs } from '../utils' + +export const TextBlock: React.FC<{ + blockId: string + block: Block + children?: React.ReactNode +}> = ({ blockId, block, children }) => { + if (!block.properties && !block.content?.length) { + return
     
    + } + + const blockColor = block.format?.block_color + + return ( +
    + {block.properties?.title && ( + + )} + + {children &&
    {children}
    } +
    + ) +} diff --git a/packages/react-notion-x/src/block-components/to-do.tsx b/packages/react-notion-x/src/block-components/to-do.tsx new file mode 100644 index 00000000..95113b3f --- /dev/null +++ b/packages/react-notion-x/src/block-components/to-do.tsx @@ -0,0 +1,40 @@ +import * as React from 'react' + +import { Block } from 'notion-types' + +import { Text } from '../components/text' +import { useNotionContext } from '../context' +import { cs } from '../utils' + +export const ToDo: React.FC<{ + blockId: string + block: Block + children: React.ReactNode +}> = ({ blockId, block, children }) => { + const ctx = useNotionContext() + + if (!block.properties) return null + + const { components } = ctx + + const isChecked = block.properties?.checked?.[0]?.[0] === 'Yes' + + return ( +
    +
    + + +
    + +
    +
    + +
    {children}
    +
    + ) +} diff --git a/packages/react-notion-x/src/block-components/toggle.tsx b/packages/react-notion-x/src/block-components/toggle.tsx new file mode 100644 index 00000000..8e65f71d --- /dev/null +++ b/packages/react-notion-x/src/block-components/toggle.tsx @@ -0,0 +1,22 @@ +import * as React from 'react' + +import { Block } from 'notion-types' + +import { Text } from '../components/text' +import { cs } from '../utils' + +export const Toggle: React.FC<{ + blockId: string + block: Block + children?: React.ReactNode +}> = ({ blockId, block, children }) => { + return ( +
    + + + + +
    {children}
    +
    + ) +} diff --git a/packages/react-notion-x/src/block.tsx b/packages/react-notion-x/src/block.tsx index 6b3e0084..35f4846d 100644 --- a/packages/react-notion-x/src/block.tsx +++ b/packages/react-notion-x/src/block.tsx @@ -1,29 +1,12 @@ import * as React from 'react' import * as types from 'notion-types' -import { - getBlockCollectionId, - getBlockIcon, - getBlockParentPage, - getPageTableOfContents, - getTextContent, - uuidToId -} from 'notion-utils' +import { uuidToId } from 'notion-utils' import { AssetWrapper } from './components/asset-wrapper' -import { Audio } from './components/audio' -import { EOI } from './components/eoi' -import { File } from './components/file' -import { GoogleDrive } from './components/google-drive' -import { LazyImage } from './components/lazy-image' -import { PageAside } from './components/page-aside' -import { PageIcon } from './components/page-icon' -import { PageTitle } from './components/page-title' import { SyncPointerBlock } from './components/sync-pointer-block' -import { Text } from './components/text' import { useNotionContext } from './context' -import { LinkIcon } from './icons/link-icon' -import { cs, getListNumber, isUrl } from './utils' +import { cs } from './utils' interface BlockProps { block: types.Block @@ -46,47 +29,12 @@ interface BlockProps { children?: React.ReactNode } -// TODO: use react state instead of a global for this -const tocIndentLevelCache: { - [blockId: string]: number -} = {} - -const pageCoverStyleCache: Record = {} - export const Block: React.FC = (props) => { const ctx = useNotionContext() - const { - components, - fullPage, - darkMode, - recordMap, - mapPageUrl, - mapImageUrl, - showTableOfContents, - minTableOfContentsItems, - defaultPageIcon, - defaultPageCover, - defaultPageCoverPosition - } = ctx - - const [activeSection, setActiveSection] = React.useState(null) - - const { - block, - children, - level, - className, - bodyClassName, - header, - footer, - pageHeader, - pageFooter, - pageTitle, - pageAside, - pageCover, - hideBlockId, - disableHeader - } = props + + const components = ctx.components + + const { block, children, level, hideBlockId } = props if (!block) { return null @@ -106,365 +54,39 @@ export const Block: React.FC = (props) => { case 'collection_view_page': // fallthrough case 'page': - if (level === 0) { - const { - page_icon = defaultPageIcon, - page_cover = defaultPageCover, - page_cover_position = defaultPageCoverPosition, - page_full_width, - page_small_text - } = block.format || {} - - if (fullPage) { - const properties = - block.type === 'page' - ? block.properties - : { - title: - recordMap.collection[getBlockCollectionId(block, recordMap)] - ?.value?.name - } - - const coverPosition = (1 - (page_cover_position || 0.5)) * 100 - const pageCoverObjectPosition = `center ${coverPosition}%` - let pageCoverStyle = pageCoverStyleCache[pageCoverObjectPosition] - if (!pageCoverStyle) { - pageCoverStyle = pageCoverStyleCache[pageCoverObjectPosition] = { - objectPosition: pageCoverObjectPosition - } - } - - const pageIcon = getBlockIcon(block, recordMap) ?? defaultPageIcon - const isPageIconUrl = pageIcon && isUrl(pageIcon) - - const toc = getPageTableOfContents( - block as types.PageBlock, - recordMap - ) - - const hasToc = - showTableOfContents && toc.length >= minTableOfContentsItems - const hasAside = (hasToc || pageAside) && !page_full_width - const hasPageCover = pageCover || page_cover - - return ( -
    -
    - -
    - {!disableHeader && } - {header} - -
    - {hasPageCover && - (pageCover ? ( - pageCover - ) : ( -
    - -
    - ))} - -
    - {page_icon && ( - - )} - - {pageHeader} - -

    - {pageTitle ?? ( - - )} -

    - - {(block.type === 'collection_view_page' || - (block.type === 'page' && - block.parent_table === 'collection')) && ( - - )} - - {block.type !== 'collection_view_page' && ( -
    -
    - {children} -
    - - {hasAside && ( - - )} -
    - )} - - {pageFooter} -
    - - {footer} -
    -
    -
    - ) - } else { - return ( -
    -
    - - {pageHeader} - - {(block.type === 'collection_view_page' || - (block.type === 'page' && - block.parent_table === 'collection')) && ( - - )} - - {block.type !== 'collection_view_page' && children} - - {pageFooter} -
    - ) - } - } else { - const blockColor = block.format?.block_color - - return ( - - - - ) - } + return case 'header': // fallthrough case 'sub_header': // fallthrough case 'sub_sub_header': { - if (!block.properties) return null - - const blockColor = block.format?.block_color - const id = uuidToId(block.id) - const title = - getTextContent(block.properties.title) || `Notion Header ${id}` - - // we use a cache here because constructing the ToC is non-trivial - let indentLevel = tocIndentLevelCache[block.id] - let indentLevelClass: string - - if (indentLevel === undefined) { - const page = getBlockParentPage(block, recordMap) - - if (page) { - const toc = getPageTableOfContents(page, recordMap) - const tocItem = toc.find((tocItem) => tocItem.id === block.id) - - if (tocItem) { - indentLevel = tocItem.indentLevel - tocIndentLevelCache[block.id] = indentLevel - } - } - } - - if (indentLevel !== undefined) { - indentLevelClass = `notion-h-indent-${indentLevel}` - } - - const isH1 = block.type === 'header' - const isH2 = block.type === 'sub_header' - const isH3 = block.type === 'sub_sub_header' - - const classNameStr = cs( - isH1 && 'notion-h notion-h1', - isH2 && 'notion-h notion-h2', - isH3 && 'notion-h notion-h3', - blockColor && `notion-${blockColor}`, - indentLevelClass, - blockId - ) - - const innerHeader = ( - -
    - {!block.format?.toggleable && ( - - - - )} - - - - - + return ( + + {children} + ) - let headerBlock = null - - //page title takes the h1 so all header blocks are greater - if (isH1) { - headerBlock = ( -

    - {innerHeader} -

    - ) - } else if (isH2) { - headerBlock = ( -

    - {innerHeader} -

    - ) - } else { - headerBlock = ( -

    - {innerHeader} -

    - ) - } - - if (block.format?.toggleable) { - return ( -
    - {headerBlock} -
    {children}
    -
    - ) - } else { - return headerBlock - } } case 'divider': - return
    + return case 'text': { - if (!block.properties && !block.content?.length) { - return
     
    - } - - const blockColor = block.format?.block_color - return ( -
    - {block.properties?.title && ( - - )} - - {children &&
    {children}
    } -
    + + {children} + ) } case 'bulleted_list': // fallthrough case 'numbered_list': { - const wrapList = (content: React.ReactNode, start?: number) => - block.type === 'bulleted_list' ? ( -
      - {content} -
    - ) : ( -
      - {content} -
    - ) - - let output: JSX.Element | null = null - - if (block.content) { - output = ( - <> - {block.properties && ( -
  • - -
  • - )} - {wrapList(children)} - - ) - } else { - output = block.properties ? ( -
  • - -
  • - ) : null - } - - const isTopLevel = - block.type !== recordMap.block[block.parent_id]?.value?.type - const start = getListNumber(block.id, recordMap.block) - - return isTopLevel ? wrapList(output, start) : output + return ( + + {children} + + ) } case 'embed': @@ -493,27 +115,21 @@ export const Block: React.FC = (props) => { return case 'drive': { - const properties = block.format?.drive_properties - if (!properties) { - //check if this drive actually needs to be embeded ex. google sheets. - if (block.format?.display_source) { - return - } - } + return + } + case 'audio': return ( - ) - } - - case 'audio': - return