Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/minimal/components/NotionPage.tsx
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
2 changes: 1 addition & 1 deletion examples/minimal/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from 'react'

// core styles shared by all of react-notion-x (required)
import 'react-notion-x/src/styles.css'
import 'areyes-react-notion-x/src/styles.css'

import '../styles/globals.css'

Expand Down
177 changes: 177 additions & 0 deletions packages/react-notion-x/src/block-components/aframe.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import * as React from 'react'

import { BaseContentBlock } from 'notion-types'

import { Text } from '../components/text'
import { useNotionContext } from '../context'

export const AFrame: React.FC<{
block: BaseContentBlock
}> = ({ block }) => {
const { recordMap } = useNotionContext()

if (!block) {
return null
}

const style: React.CSSProperties = {
position: 'relative',
display: 'flex',
justifyContent: 'center',
alignSelf: 'center',
width: '100%',
maxWidth: '100%',
flexDirection: 'column'
}

const assetStyle: React.CSSProperties = {}
// console.log('asset', block)

if (block.format) {
const {
block_aspect_ratio,
block_height,
block_width,
block_full_width,
block_page_width,
block_preserve_scale
} = block.format

if (block_full_width || block_page_width) {
if (block_full_width) {
style.width = '100vw'
} else {
style.width = '100%'
}

if (block.type === 'video') {
if (block_height) {
style.height = block_height
} else if (block_aspect_ratio) {
style.paddingBottom = `${block_aspect_ratio * 100}%`
} else if (block_preserve_scale) {
style.objectFit = 'contain'
}
} else if (block_aspect_ratio && block.type !== 'image') {
style.paddingBottom = `${block_aspect_ratio * 100}%`
} else if (block_height) {
style.height = block_height
} else if (block_preserve_scale) {
if (block.type === 'image') {
style.height = '100%'
} else {
// TODO: this is just a guess
style.paddingBottom = '75%'
style.minHeight = 100
}
}
} else {
switch (block.format?.block_alignment) {
case 'center': {
style.alignSelf = 'center'
break
}
case 'left': {
style.alignSelf = 'start'
break
}
case 'right': {
style.alignSelf = 'end'
break
}
}

if (block_width) {
style.width = block_width
}

if (block_preserve_scale && block.type !== 'image') {
style.paddingBottom = '50%'
style.minHeight = 100
} else {
if (block_height && block.type !== 'image') {
style.height = block_height
}
}
}

if (block.type === 'image') {
assetStyle.objectFit = 'cover'
} else if (block_preserve_scale) {
assetStyle.objectFit = 'contain'
}
}

const source =
recordMap.signed_urls?.[block.id] || block.properties?.source?.[0]?.[0]
let content = null

if (!source) {
return null
}

let src = block.format?.display_source || source

if (src) {
if (block.type === 'gist') {
if (!src.endsWith('.pibb')) {
src = `${src}.pibb`
}

assetStyle.width = '100%'
style.paddingBottom = '50%'

// TODO: GitHub gists do not resize their height properly
content = (
<iframe
style={assetStyle}
className='notion-asset-object-fit'
src={src}
title='GitHub Gist'
frameBorder='0'
// TODO: is this sandbox necessary?
// sandbox='allow-scripts allow-popups allow-top-navigation-by-user-activation allow-forms allow-same-origin'
// this is important for perf but react's TS definitions don't seem to like it
loading='lazy'
scrolling='auto'
/>
)
} else {
src += block.type === 'typeform' ? '&disable-auto-focus=true' : ''

content = (
<iframe
className='notion-asset-object-fit'
style={assetStyle}
src={src}
title={`iframe ${block.type}`}
frameBorder='0'
// TODO: is this sandbox necessary?
// sandbox='allow-scripts allow-popups allow-top-navigation-by-user-activation allow-forms allow-same-origin'
allowFullScreen
// this is important for perf but react's TS definitions don't seem to like it
loading='lazy'
scrolling='auto'
/>
)
}
}

const children = (
<>
{block?.properties?.caption && (
<figcaption className='notion-asset-caption'>
<Text value={block.properties.caption} block={block as any} />
</figcaption>
)}
</>
)

return (
<>
<div style={style}>{content}</div>

{children}
</>
)
}
31 changes: 31 additions & 0 deletions packages/react-notion-x/src/block-components/alias.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<components.PageLink
className={cs('notion-page-link', blockPointerId)}
href={mapPageUrl(blockPointerId)}
>
<PageTitle block={linkedBlock} />
</components.PageLink>
)
}
24 changes: 24 additions & 0 deletions packages/react-notion-x/src/block-components/asset-wrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import * as React from 'react'

import { Block } from 'notion-types'

import { cs } from '../utils'

export const AssetWrapper: React.FC<{
blockId: string
block: Block
children: any
}> = ({ blockId, block, children }) => {
return (
<figure
className={cs(
'notion-asset-wrapper',
`notion-asset-wrapper-${block.type}`,
block.format?.block_full_width && 'notion-asset-wrapper-full',
blockId
)}
>
{children}
</figure>
)
}
94 changes: 94 additions & 0 deletions packages/react-notion-x/src/block-components/bookmark.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className='notion-row'>
<components.Link
target='_blank'
rel='noopener noreferrer'
className={cs(
'notion-bookmark',
block.format?.block_color && `notion-${block.format.block_color}`,
blockId
)}
href={link[0][0]}
>
<div>
{title && (
<div className='notion-bookmark-title'>
<Text value={[[title]]} block={block} />
</div>
)}

{block.properties?.description && (
<div className='notion-bookmark-description'>
<Text value={block.properties?.description} block={block} />
</div>
)}

<div className='notion-bookmark-link'>
{block.format?.bookmark_icon && (
<div className='notion-bookmark-link-icon'>
<LazyImage
src={mapImageUrl(block.format?.bookmark_icon, block)}
alt={title}
/>
</div>
)}

<div className='notion-bookmark-link-text'>
<Text value={link} block={block} />
</div>
</div>
</div>

{block.format?.bookmark_cover && (
<div className='notion-bookmark-image'>
<LazyImage
src={mapImageUrl(block.format?.bookmark_cover, block)}
alt={getTextContent(block.properties?.title)}
style={{
objectFit: 'cover'
}}
/>
</div>
)}
</components.Link>
</div>
)
}
30 changes: 30 additions & 0 deletions packages/react-notion-x/src/block-components/callout.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div
className={cs(
'notion-callout',
block.format?.block_color && `notion-${block.format?.block_color}_co`,
blockId
)}
>
<PageIcon block={block} />

<div className='notion-callout-text'>
<Text value={block.properties?.title} block={block} />
{children}
</div>
</div>
)
}
13 changes: 13 additions & 0 deletions packages/react-notion-x/src/block-components/column-list.tsx
Original file line number Diff line number Diff line change
@@ -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 <div className={cs('notion-row', blockId)}>{children}</div>
}
Loading