Skip to content
Snippets Groups Projects
PortableTextBlock.js 5.14 KiB
Newer Older
Tarje Lavik's avatar
Tarje Lavik committed
import React from 'react'
Tarje.Lavik's avatar
Tarje.Lavik committed
import { kebabCase } from 'lodash'
Tarje.Lavik's avatar
Tarje.Lavik committed
import Link from '../Link'
import { Heading, Text } from '@chakra-ui/react'
Tarje.Lavik's avatar
Tarje.Lavik committed
import {
  BigText,
  Hero,
  Iframe,
  InstagramPost,
  PageHeader,
  Quote,
  MiradorGallery,
  SectionText,
  SingleLevelChart,
  SingleObject,
  Social,
  SubStory,
Tarje.Lavik's avatar
Tarje.Lavik committed
  TimelineSection,
  TwoColumn,
  Video,
  IllustrationWithCaption,
  ExhibitionElement,
Tarje Lavik's avatar
Tarje Lavik committed
  Gallery,
Tarje.Lavik's avatar
Tarje.Lavik committed
} from '../Sections'
import ActorInsert from './Inserts/ActorInsert'
import PlaceInsert from './Inserts/PlaceInsert'
import { getFootnotesNumberArray } from '../../lib/utils'
Sanity.io's avatar
Sanity.io committed

const BlockContent = require('@sanity/block-content-to-react')

export default function PortableTextBlock(props) {
Tarje.Lavik's avatar
Tarje.Lavik committed
  if (!props.blocks || !Array.isArray(props.blocks) || !props.blocks.length) {
Sanity.io's avatar
Sanity.io committed
    return null
  }

    fontSize = { base: 'lg', sm: 'xl', md: 'xl', xl: '2xl' },
    maxW = ['md', null, '2xl', null],
Tarje.Lavik's avatar
Tarje.Lavik committed
    lineHeight = ['1.25', '1.3'],
    fontWeight = 'normal',
Sanity.io's avatar
Sanity.io committed

  const footnoteNumbers = getFootnotesNumberArray(props.blocks)

Tarje Lavik's avatar
Tarje Lavik committed
  const getFontSize = (level) => {
Tarje.Lavik's avatar
Tarje.Lavik committed
    switch (level) {
      case 'h2':
        return { base: 'xl', sm: '2xl', md: '3xl', xl: '4xl' }
Tarje.Lavik's avatar
Tarje.Lavik committed
      case 'h3':
        return { base: 'lg', sm: 'xl', md: '2xl', xl: '3xl' }
Tarje.Lavik's avatar
Tarje.Lavik committed
      default:
        return null
Sanity.io's avatar
Sanity.io committed
  const BlockRenderer = (props) => {
Tarje.Lavik's avatar
Tarje.Lavik committed
    if (!props) {
      return null
    }
    const { style = 'normal' } = props.node
Sanity.io's avatar
Sanity.io committed

    if (/^h\d/.test(style)) {
Tarje Lavik's avatar
Tarje Lavik committed
      const level = style
Tarje.Lavik's avatar
Tarje.Lavik committed
      // TODO: This is prone to breaking when there is both hX and strong
      const id = kebabCase(props.children)
      return (
        <Heading id={id} maxW={maxW} margin="auto" as={level} fontSize={getFontSize(level)}>
          {props.children}
Tarje.Lavik's avatar
Tarje.Lavik committed
        </Heading>
      )
Sanity.io's avatar
Sanity.io committed
    }

    if (style === 'blockquote') {
      return <blockquote>- {props.children}</blockquote>
    }

    return (
Tarje.Lavik's avatar
Tarje.Lavik committed
      <Text
        maxW={maxW}
Tarje.Lavik's avatar
Tarje.Lavik committed
        fontSize={fontSize}
        lineHeight={lineHeight}
        fontWeight={fontWeight}
        fontFamily={fontFamily}
        mx={mx ?? 'auto'}
Sanity.io's avatar
Sanity.io committed
        {props.children}
      </Text>
    )
    // Fall back to default handling
    // return BlockContent.defaultSerializers.types.block(props)
  }

  const serializers = {
    marks: {
Tarje.Lavik's avatar
Tarje.Lavik committed
      internalLink: ({ mark, children }) => {
        const { reference } = mark
Sanity.io's avatar
Sanity.io committed
        const href = `/id/${reference._ref}`
        const text = children.length ? children[0] : children
Tarje.Lavik's avatar
Tarje.Lavik committed
        return <Link href={href}>{text}</Link>
Sanity.io's avatar
Sanity.io committed
      },
Tarje.Lavik's avatar
Tarje.Lavik committed
      link: ({ mark, children }) => {
Tarje.Lavik's avatar
Tarje.Lavik committed
        // console.log(children)
Sanity.io's avatar
Sanity.io committed
        // Read https://css-tricks.com/use-target_blank/
Tarje.Lavik's avatar
Tarje.Lavik committed
        const { blank, href } = mark
        const text = children.length ? children[0] : children
Sanity.io's avatar
Sanity.io committed
        return blank ? (
          <Link href={href} isExternal>
Sanity.io's avatar
Sanity.io committed
          </Link>
        ) : (
Tarje.Lavik's avatar
Tarje.Lavik committed
          <Link href={href}>{text}</Link>
Sanity.io's avatar
Sanity.io committed
        )
      },
      footnote: ({ children, markKey }) => {
        console.log(markKey)
        return (
          <span>
            {children}
            <sup>
              {/* 
              https://codesandbox.io/s/how-to-render-footnotes-in-portable-text-in-react-iupur?file=/src/index.js:254-650
              If you want numbers here, you can reuse the reduce function from Footnotes.js
              to e.g. an object with markKey as keys and the index as values.
              {[markKey]: index}. 
            */}
              <a href={`#${markKey}`}>{footnoteNumbers[markKey]}</a>
            </sup>
          </span>
        )
      },
Sanity.io's avatar
Sanity.io committed
    },
    types: {
      code: (props) => (
        <pre data-language={props.node.language}>
          <code>{props.node.code}</code>
        </pre>
      ),
      block: BlockRenderer,
      ActorCollection: (props) => <ActorCollection {...props.node} />,
Tarje.Lavik's avatar
Tarje.Lavik committed
      BigText: (props) => <BigText {...props.node} />,
      ExhibitionElement: (props) => <ExhibitionElement {...props.node} />,
      Hero: (props) => <Hero {...props.node} />,
      Iframe: (props) => <Iframe {...props.node} />,
      IllustrationWithCaption: (props) => <IllustrationWithCaption {...props.node} />,
      InstagramPost: (props) => <InstagramPost {...props.node} />,
Tarje Lavik's avatar
Tarje Lavik committed
      Gallery: (props) => <Gallery {...props.node} />,
Tarje.Lavik's avatar
Tarje.Lavik committed
      MiradorGallery: (props) => <MiradorGallery {...props.node} />,
      PageHeader: (props) => <PageHeader {...props.node} />,
      Quote: (props) => <Quote {...props.node} />,
      SectionText: (props) => <SectionText {...props.node} />,
      SingleLevelChart: (props) => <SingleLevelChart {...props.node} />,
      SingleObject: (props) => <SingleObject {...props.node} />,
      Social: (props) => <Social {...props.node} />,
      SubStory: (props) => <SubStory {...props.node} />,
      Table: (props) => <Table {...props.node} />,
Tarje.Lavik's avatar
Tarje.Lavik committed
      TimelineSection: (props) => <TimelineSection {...props.node} />,
      TwoColumn: (props) => <TwoColumn {...props.node} />,
      Video: (props) => <Video {...props.node} />,
Tarje.Lavik's avatar
Tarje.Lavik committed
      Actor: (props) => <ActorInsert {...props.node} />,
      Place: (props) => <PlaceInsert {...props.node} />,
Sanity.io's avatar
Sanity.io committed
    },
  }

  return <BlockContent blocks={blocks} serializers={serializers} />
Sanity.io's avatar
Sanity.io committed
}