Skip to content

Instantly share code, notes, and snippets.

@dimaip
Created February 16, 2021 07:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dimaip/0454b58d360f26c1e6a87310ace61f8c to your computer and use it in GitHub Desktop.
Save dimaip/0454b58d360f26c1e6a87310ace61f8c to your computer and use it in GitHub Desktop.
Keystone SlateJS v0.47 sample rendering
import React, { useContext } from 'react'
interface Mark {
type: string
}
export interface ElementNode {
type: string
nodes: Array<ElementNode | LeafNode>
data: {
[item: string]: string
}
}
export interface LeafNode {
type: string
text: string
marks: Mark[]
}
interface RenderElementArgs {
nodes: JSX.Element
element: ElementNode
}
interface RenderLeafArgs {
nodes: JSX.Element
leaf: LeafNode
}
interface SlatePresentationContextType {
renderElement: (props: RenderElementArgs) => JSX.Element
renderLeaf: (props: RenderLeafArgs) => JSX.Element
}
const SlatePresentationContext = React.createContext<SlatePresentationContextType>({
renderElement: (props: RenderElementArgs) => <DefaultElement {...props} />,
renderLeaf: (props: RenderLeafArgs) => <DefaultLeaf {...props} />,
})
const useSlatePresentation = (): SlatePresentationContextType =>
useContext<SlatePresentationContextType>(SlatePresentationContext)
const isElement = (value: ElementNode | LeafNode): value is ElementNode =>
value instanceof Object && 'nodes' in value && Array.isArray(value.nodes)
function Element({ element }: { element: ElementNode }): JSX.Element {
const { renderElement } = useSlatePresentation()
return <>{renderElement({ nodes: <Children nodes={element.nodes} />, element })}</>
}
function Leaf({ leaf }: { leaf: LeafNode }): JSX.Element {
const { renderLeaf } = useSlatePresentation()
return <>{renderLeaf({ nodes: <span>{leaf.text}</span>, leaf })}</>
}
function Children({ nodes = [] }: { nodes: Array<ElementNode | LeafNode> }): JSX.Element {
return (
<>
{nodes.map((child, i) => {
if (isElement(child)) {
return <Element key={i} element={child} />
} else {
return <Leaf key={i} leaf={child} />
}
})}
</>
)
}
export function SlateReactPresentation({
nodes = [],
renderElement = (props) => <DefaultElement {...props} />,
renderLeaf = (props) => <DefaultLeaf {...props} />,
}: {
nodes: Array<ElementNode | LeafNode>
renderElement: (props: RenderElementArgs) => JSX.Element
renderLeaf: (props: RenderLeafArgs) => JSX.Element
}): JSX.Element {
return (
<SlatePresentationContext.Provider value={{ renderElement, renderLeaf }}>
<Children nodes={nodes} />
</SlatePresentationContext.Provider>
)
}
function DefaultElement({ nodes, element }: RenderElementArgs): JSX.Element {
return <div>{nodes}</div>
}
function DefaultLeaf({ nodes, leaf }: RenderLeafArgs): JSX.Element {
return <span>{nodes}</span>
}
<SlateReactPresentation
nodes={nodes}
renderElement={(props) => {
const { element, nodes } = props
switch (element.type) {
case 'heading':
return <Typography variant="h3">{nodes}</Typography>
case 'paragraph':
return <Typography variant="body1">{nodes}</Typography>
case 'blockquote':
return (
<blockquote
css={(theme: Theme) => css`
border-left: 4px solid ${theme.palette.divider};
margin-left: 0;
padding-left: ${theme.spacing(2)}px;
`}
>
<Typography variant="body1">{nodes}</Typography>
</blockquote>
)
case 'list-item':
return (
<li
css={(theme: Theme) =>
css`
margin-left: ${theme.spacing(4)}px;
`
}
>
{nodes}
</li>
)
case 'unordered-list':
return (
<Typography variant="body1">
<ul
css={css`
list-style-type: circle;
& > li {
list-style-type: circle;
}
`}
>
{nodes}
</ul>
</Typography>
)
case 'ordered-list':
return (
<Typography variant="body1">
<ol
css={css`
list-style-type: decimal;
& > li {
list-style-type: decimal;
}
`}
>
{nodes}
</ol>
</Typography>
)
case 'link':
return (
<Link href={element.data?.href} passHref>
<a>{nodes}</a>
</Link>
)
default:
return <div>{nodes}</div>
}
}}
renderLeaf={(props) => {
const { leaf } = props
let result = <>{leaf.text}</>
leaf.marks.forEach((mark) => {
switch (mark.type) {
case 'bold':
result = <strong>{result}</strong>
break
case 'italic':
result = <em>{result}</em>
break
case 'underline':
result = <u>{result}</u>
break
case 'strikethrough':
result = <s>{result}</s>
break
default:
result = <span>{result}</span>
}
})
return result
}}
/>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment