Migrate to a monorepo setup - step 1 * Move web to subfolder /apps/scandic-web * Yarn + transitive deps - Move to yarn - design-system package removed for now since yarn doesn't support the parameter for token (ie project currently broken) - Add missing transitive dependencies as Yarn otherwise prevents these imports - VS Code doesn't pick up TS path aliases unless you open /apps/scandic-web instead of root (will be fixed with monorepo) * Pin framer-motion to temporarily fix typing issue https://github.com/adobe/react-spectrum/issues/7494 * Pin zod to avoid typ error There seems to have been a breaking change in the types returned by zod where error is now returned as undefined instead of missing in the type. We should just handle this but to avoid merge conflicts just pin the dependency for now. * Pin react-intl version Pin version of react-intl to avoid tiny type issue where formatMessage does not accept a generic any more. This will be fixed in a future commit, but to avoid merge conflicts just pin for now. * Pin typescript version Temporarily pin version as newer versions as stricter and results in a type error. Will be fixed in future commit after merge. * Setup workspaces * Add design-system as a monorepo package * Remove unused env var DESIGN_SYSTEM_ACCESS_TOKEN * Fix husky for monorepo setup * Update netlify.toml * Add lint script to root package.json * Add stub readme * Fix react-intl formatMessage types * Test netlify.toml in root * Remove root toml * Update netlify.toml publish path * Remove package-lock.json * Update build for branch/preview builds Approved-by: Linus Flood
769 lines
20 KiB
TypeScript
769 lines
20 KiB
TypeScript
import Image from "@/components/Image"
|
|
import Link from "@/components/TempDesignSystem/Link"
|
|
import { insertResponseToImageVaultAsset } from "@/utils/imageVault"
|
|
|
|
import { OpenInNewSmallIcon } from "../Icons"
|
|
import ImageContainer from "../ImageContainer"
|
|
import Divider from "../TempDesignSystem/Divider"
|
|
import Table from "../TempDesignSystem/Table"
|
|
import BiroScript from "../TempDesignSystem/Text/BiroScript"
|
|
import Body from "../TempDesignSystem/Text/Body"
|
|
import Caption from "../TempDesignSystem/Text/Caption"
|
|
import Footnote from "../TempDesignSystem/Text/Footnote"
|
|
import Subtitle from "../TempDesignSystem/Text/Subtitle"
|
|
import Title from "../TempDesignSystem/Text/Title"
|
|
import {
|
|
hasAvailableParagraphFormat,
|
|
hasAvailableULFormat,
|
|
makeCssModuleCompatibleClassName,
|
|
} from "./utils"
|
|
|
|
import styles from "./jsontohtml.module.css"
|
|
|
|
import { ContentEnum } from "@/types/enums/content"
|
|
import type { EmbedByUid } from "@/types/transitionTypes/jsontohtml"
|
|
import type {
|
|
Attributes,
|
|
RTEImageVaultAttrs,
|
|
} from "@/types/transitionTypes/rte/attrs"
|
|
import {
|
|
AvailableParagraphFormatEnum,
|
|
RTEItemTypeEnum,
|
|
RTETypeEnum,
|
|
} from "@/types/transitionTypes/rte/enums"
|
|
import {
|
|
type RTEDefaultNode,
|
|
type RTEImageNode,
|
|
RTEMarkType,
|
|
type RTENext,
|
|
type RTENode,
|
|
type RTERegularNode,
|
|
type RTETextNode,
|
|
} from "@/types/transitionTypes/rte/node"
|
|
import type { RenderOptions } from "@/types/transitionTypes/rte/option"
|
|
|
|
function noNestedLinksOrReferences(node: RTENode) {
|
|
if ("type" in node) {
|
|
if (node.type === RTETypeEnum.reference) {
|
|
return node.children
|
|
} else if (node.type === RTETypeEnum.a) {
|
|
return node.children
|
|
}
|
|
}
|
|
return node
|
|
}
|
|
|
|
function extractPossibleAttributes(attrs: Attributes | undefined) {
|
|
if (!attrs) return {}
|
|
const props: Record<string, any> = {}
|
|
if (attrs.id) {
|
|
props.id = attrs.id
|
|
}
|
|
|
|
if (attrs.class) {
|
|
props.className = attrs.class
|
|
} else if (attrs["class-name"]) {
|
|
props.className = attrs["class-name"]
|
|
} else if (attrs.classname) {
|
|
props.className = attrs.classname
|
|
}
|
|
|
|
if (attrs.style?.["text-align"]) {
|
|
props.style = {
|
|
textAlign: attrs.style["text-align"],
|
|
}
|
|
}
|
|
|
|
return props
|
|
}
|
|
|
|
export const renderOptions: RenderOptions = {
|
|
[RTETypeEnum.a]: (
|
|
node: RTERegularNode,
|
|
embeds: EmbedByUid,
|
|
next: RTENext,
|
|
fullRenderOptions: RenderOptions
|
|
) => {
|
|
if (node.attrs.url) {
|
|
const props = extractPossibleAttributes(node.attrs)
|
|
return (
|
|
<Link
|
|
{...props}
|
|
href={node.attrs.url}
|
|
key={node.uid}
|
|
target={node.attrs.target ?? "_blank"}
|
|
variant="underscored"
|
|
color="burgundy"
|
|
>
|
|
{next(
|
|
// Sometimes editors happen to nest a reference inside a link and vice versa.
|
|
// In that case use the outermost link, i.e. ignore nested links.
|
|
node.children.flatMap(noNestedLinksOrReferences),
|
|
embeds,
|
|
fullRenderOptions
|
|
)}
|
|
</Link>
|
|
)
|
|
}
|
|
return null
|
|
},
|
|
|
|
[RTETypeEnum.blockquote]: (
|
|
node: RTEDefaultNode,
|
|
embeds: EmbedByUid,
|
|
next: RTENext,
|
|
fullRenderOptions: RenderOptions
|
|
) => {
|
|
const props = extractPossibleAttributes(node.attrs)
|
|
return (
|
|
<BiroScript key={node.uid} {...props}>
|
|
{next(node.children, embeds, fullRenderOptions)}
|
|
</BiroScript>
|
|
)
|
|
},
|
|
|
|
[RTETypeEnum.code]: (
|
|
node: RTEDefaultNode,
|
|
embeds: EmbedByUid,
|
|
next: RTENext,
|
|
fullRenderOptions: RenderOptions
|
|
) => {
|
|
const props = extractPossibleAttributes(node.attrs)
|
|
return (
|
|
<code key={node.uid} {...props}>
|
|
{next(node.children, embeds, fullRenderOptions)}
|
|
</code>
|
|
)
|
|
},
|
|
|
|
[RTETypeEnum.embed]: (
|
|
node: RTEDefaultNode,
|
|
embeds: EmbedByUid,
|
|
next: RTENext,
|
|
fullRenderOptions: RenderOptions
|
|
) => {
|
|
const props = extractPossibleAttributes(node.attrs)
|
|
if (node.attrs.src) {
|
|
props.src = node.attrs.src
|
|
}
|
|
if (node.attrs.url) {
|
|
props.src = node.attrs.url
|
|
}
|
|
if (!props.src) {
|
|
return null
|
|
}
|
|
return (
|
|
<iframe key={node.uid} {...props}>
|
|
{next(node.children, embeds, fullRenderOptions)}
|
|
</iframe>
|
|
)
|
|
},
|
|
|
|
[RTETypeEnum.h1]: (
|
|
node: RTEDefaultNode,
|
|
embeds: EmbedByUid,
|
|
next: RTENext,
|
|
fullRenderOptions: RenderOptions
|
|
) => {
|
|
const props = extractPossibleAttributes(node.attrs)
|
|
return (
|
|
<Title key={node.uid} {...props} level="h1">
|
|
{next(node.children, embeds, fullRenderOptions)}
|
|
</Title>
|
|
)
|
|
},
|
|
|
|
[RTETypeEnum.h2]: (
|
|
node: RTEDefaultNode,
|
|
embeds: EmbedByUid,
|
|
next: RTENext,
|
|
fullRenderOptions: RenderOptions
|
|
) => {
|
|
const props = extractPossibleAttributes(node.attrs)
|
|
return (
|
|
<Title key={node.uid} {...props} level="h2">
|
|
{next(node.children, embeds, fullRenderOptions)}
|
|
</Title>
|
|
)
|
|
},
|
|
|
|
[RTETypeEnum.h3]: (
|
|
node: RTEDefaultNode,
|
|
embeds: EmbedByUid,
|
|
next: RTENext,
|
|
fullRenderOptions: RenderOptions
|
|
) => {
|
|
const props = extractPossibleAttributes(node.attrs)
|
|
return (
|
|
<Title key={node.uid} {...props} level="h3">
|
|
{next(node.children, embeds, fullRenderOptions)}
|
|
</Title>
|
|
)
|
|
},
|
|
|
|
[RTETypeEnum.h4]: (
|
|
node: RTEDefaultNode,
|
|
embeds: EmbedByUid,
|
|
next: RTENext,
|
|
fullRenderOptions: RenderOptions
|
|
) => {
|
|
const props = extractPossibleAttributes(node.attrs)
|
|
return (
|
|
<Title key={node.uid} {...props} level="h4">
|
|
{next(node.children, embeds, fullRenderOptions)}
|
|
</Title>
|
|
)
|
|
},
|
|
|
|
[RTETypeEnum.h5]: (
|
|
node: RTEDefaultNode,
|
|
embeds: EmbedByUid,
|
|
next: RTENext,
|
|
fullRenderOptions: RenderOptions
|
|
) => {
|
|
const props = extractPossibleAttributes(node.attrs)
|
|
return (
|
|
<Title key={node.uid} {...props} level="h5">
|
|
{next(node.children, embeds, fullRenderOptions)}
|
|
</Title>
|
|
)
|
|
},
|
|
|
|
[RTETypeEnum.hr]: () => {
|
|
return <Divider />
|
|
},
|
|
|
|
[RTETypeEnum.li]: (
|
|
node: RTEDefaultNode,
|
|
embeds: EmbedByUid,
|
|
next: RTENext,
|
|
fullRenderOptions: RenderOptions
|
|
) => {
|
|
const props = extractPossibleAttributes(node.attrs)
|
|
const compatibleClassName = makeCssModuleCompatibleClassName(
|
|
props.className,
|
|
"ul"
|
|
)
|
|
return (
|
|
<li
|
|
key={node.uid}
|
|
{...props}
|
|
className={`${styles.li} ${compatibleClassName}`}
|
|
>
|
|
{next(node.children, embeds, fullRenderOptions)}
|
|
</li>
|
|
)
|
|
},
|
|
|
|
[RTETypeEnum.ol]: (
|
|
node: RTEDefaultNode,
|
|
embeds: EmbedByUid,
|
|
next: RTENext,
|
|
fullRenderOptions: RenderOptions
|
|
) => {
|
|
const props = extractPossibleAttributes(node.attrs)
|
|
|
|
// Set the number of rows dynamically to create even rows for each column. We want the li:s
|
|
// to flow with the column, so therefore this is needed.
|
|
let numberOfRows: number | undefined
|
|
if (node.children.length > 4) {
|
|
const half = node.children.length / 2
|
|
numberOfRows = Math.ceil(half)
|
|
}
|
|
|
|
return (
|
|
<ol
|
|
key={node.uid}
|
|
{...props}
|
|
className={styles.ol}
|
|
style={
|
|
numberOfRows
|
|
? { gridTemplateRows: `repeat(${numberOfRows}, auto)` }
|
|
: {}
|
|
}
|
|
>
|
|
{next(node.children, embeds, fullRenderOptions)}
|
|
</ol>
|
|
)
|
|
},
|
|
|
|
[RTETypeEnum.p]: (
|
|
node: RTEDefaultNode,
|
|
embeds: EmbedByUid,
|
|
next: RTENext,
|
|
fullRenderOptions: RenderOptions
|
|
) => {
|
|
const props = extractPossibleAttributes(node.attrs)
|
|
|
|
const hasFormat = node.children.some((item) =>
|
|
hasAvailableParagraphFormat((item as RTETextNode)?.classname)
|
|
)
|
|
|
|
// If a child node has an available format as className, we wrap it in a
|
|
// span and render the children with the correct component
|
|
if (hasFormat) {
|
|
return next(node.children, embeds, fullRenderOptions)
|
|
}
|
|
|
|
return (
|
|
<Body {...props} key={node.uid}>
|
|
{next(node.children, embeds, fullRenderOptions)}
|
|
</Body>
|
|
)
|
|
},
|
|
|
|
[RTETypeEnum.span]: (
|
|
node: RTEDefaultNode,
|
|
embeds: EmbedByUid,
|
|
next: RTENext,
|
|
fullRenderOptions: RenderOptions
|
|
) => {
|
|
let props = extractPossibleAttributes(node.attrs)
|
|
const className = props.className
|
|
|
|
if (className) {
|
|
if (hasAvailableULFormat(className)) {
|
|
// @ts-ignore: We want to set css modules classNames even if it does not correspond
|
|
// to an existing class in the module style sheet. Due to our css modules plugin for
|
|
// typescript, we cannot do this without the ts-ignore
|
|
props.className = styles[className]
|
|
}
|
|
}
|
|
|
|
return (
|
|
<span {...props}>{next(node.children, embeds, fullRenderOptions)}</span>
|
|
)
|
|
},
|
|
|
|
[RTETypeEnum.reference]: (
|
|
node: RTENode,
|
|
embeds: EmbedByUid,
|
|
next: RTENext,
|
|
fullRenderOptions: RenderOptions
|
|
) => {
|
|
if ("attrs" in node) {
|
|
const type = node.attrs.type
|
|
if (type === RTEItemTypeEnum.asset) {
|
|
const imageTypeRegex = /^image\//
|
|
const isImage = imageTypeRegex.test(node.attrs["asset-type"])
|
|
if (isImage) {
|
|
const image = embeds?.[node?.attrs?.["asset-uid"]]
|
|
if (image?.node.__typename === ContentEnum.blocks.SysAsset) {
|
|
if (image.node.url) {
|
|
const alt = image?.node?.title ?? node.attrs.alt
|
|
const props = extractPossibleAttributes(node.attrs)
|
|
props.className = styles.image
|
|
return (
|
|
<div className={styles.imageContainer}>
|
|
<Image
|
|
alt={alt}
|
|
className={styles.image}
|
|
src={image.node.url}
|
|
fill
|
|
sizes="(min-width: 1367px) 800px, (max-width: 1366px) and (min-width: 1200px) 1200px, 100vw"
|
|
{...props}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|
|
}
|
|
} else if (node.attrs["display-type"] === "link" && node.attrs.href) {
|
|
return (
|
|
<Link
|
|
href={node.attrs.href}
|
|
key={node.uid}
|
|
variant="icon"
|
|
textDecoration="underline"
|
|
color="burgundy"
|
|
target="_blank"
|
|
>
|
|
{next(
|
|
// Sometimes editors happen to nest a reference inside a link and vice versa.
|
|
// In that case use the outermost link, i.e. ignore nested links.
|
|
node.children.flatMap(noNestedLinksOrReferences),
|
|
embeds,
|
|
fullRenderOptions
|
|
)}
|
|
<OpenInNewSmallIcon color="burgundy" />
|
|
</Link>
|
|
)
|
|
}
|
|
} else if (type === RTEItemTypeEnum.entry) {
|
|
const entry = embeds?.[node?.attrs?.["entry-uid"]]
|
|
|
|
if (entry?.node.__typename === ContentEnum.blocks.ImageContainer) {
|
|
if (entry.node.image_left && entry.node.image_right) {
|
|
return (
|
|
<ImageContainer
|
|
leftImage={entry.node.image_left}
|
|
rightImage={entry.node.image_right}
|
|
/>
|
|
)
|
|
}
|
|
return null
|
|
} else if (
|
|
entry?.node.__typename === ContentEnum.blocks.AccountPage ||
|
|
entry?.node.__typename === ContentEnum.blocks.CollectionPage ||
|
|
entry?.node.__typename === ContentEnum.blocks.ContentPage ||
|
|
entry?.node.__typename === ContentEnum.blocks.DestinationCityPage ||
|
|
entry?.node.__typename ===
|
|
ContentEnum.blocks.DestinationCountryPage ||
|
|
entry?.node.__typename ===
|
|
ContentEnum.blocks.DestinationOverviewPage ||
|
|
entry?.node.__typename === ContentEnum.blocks.HotelPage ||
|
|
entry?.node.__typename === ContentEnum.blocks.LoyaltyPage ||
|
|
entry?.node.__typename === ContentEnum.blocks.StartPage
|
|
) {
|
|
// If entry is not an ImageContainer, it is a page and we return it as a link
|
|
const props = extractPossibleAttributes(node.attrs)
|
|
|
|
return (
|
|
<Link
|
|
{...props}
|
|
href={entry.node.url}
|
|
key={node.uid}
|
|
variant="underscored"
|
|
color="burgundy"
|
|
>
|
|
{next(
|
|
// Sometimes editors happen to nest a reference inside a link and vice versa.
|
|
// In that case use the outermost link, i.e. ignore nested links.
|
|
node.children.flatMap(noNestedLinksOrReferences),
|
|
embeds,
|
|
fullRenderOptions
|
|
)}
|
|
</Link>
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
return null
|
|
},
|
|
|
|
[RTETypeEnum.ImageVault]: (node: RTEImageNode) => {
|
|
if ("attrs" in node) {
|
|
const type = node.type
|
|
if (type === RTETypeEnum.ImageVault) {
|
|
const attrs = node.attrs as RTEImageVaultAttrs
|
|
let image = undefined
|
|
if ("dimensions" in attrs) {
|
|
image = attrs
|
|
} else {
|
|
image = insertResponseToImageVaultAsset(attrs)
|
|
}
|
|
const alt = image.meta.alt ?? image.title
|
|
const props = extractPossibleAttributes(attrs)
|
|
return (
|
|
<section key={node.uid}>
|
|
<div className={styles.imageContainer}>
|
|
<Image
|
|
alt={alt}
|
|
className={styles.image}
|
|
src={image.url}
|
|
fill
|
|
sizes="(min-width: 1367px) 800px, (max-width: 1366px) and (min-width: 1200px) 1200px, 100vw"
|
|
focalPoint={image.focalPoint}
|
|
{...props}
|
|
/>
|
|
</div>
|
|
<Caption>{image.meta.caption}</Caption>
|
|
</section>
|
|
)
|
|
}
|
|
}
|
|
|
|
return null
|
|
},
|
|
|
|
[RTETypeEnum.table]: (
|
|
node: RTEDefaultNode,
|
|
embeds: EmbedByUid,
|
|
next: RTENext,
|
|
fullRenderOptions: RenderOptions
|
|
) => {
|
|
const props = extractPossibleAttributes(node.attrs)
|
|
return (
|
|
<div className={styles.tableContainer}>
|
|
<Table key={node.uid} {...props}>
|
|
{next(node.children, embeds, fullRenderOptions)}
|
|
</Table>
|
|
</div>
|
|
)
|
|
},
|
|
|
|
[RTETypeEnum.thead]: (
|
|
node: RTEDefaultNode,
|
|
embeds: EmbedByUid,
|
|
next: RTENext,
|
|
fullRenderOptions: RenderOptions
|
|
) => {
|
|
// Override the styling of p tags inside the thead tag
|
|
const theadChildPRenderOptions = {
|
|
...fullRenderOptions,
|
|
[RTETypeEnum.p]: (
|
|
node: RTEDefaultNode,
|
|
embeds: EmbedByUid,
|
|
next: RTENext,
|
|
fullRenderOptions: RenderOptions
|
|
) => (
|
|
<Body color={"burgundy"} textTransform={"bold"}>
|
|
{next(node.children, embeds, fullRenderOptions)}
|
|
</Body>
|
|
),
|
|
}
|
|
const props = extractPossibleAttributes(node.attrs)
|
|
return (
|
|
<Table.THead key={node.uid} {...props}>
|
|
{next(node.children, embeds, theadChildPRenderOptions)}
|
|
</Table.THead>
|
|
)
|
|
},
|
|
|
|
[RTETypeEnum.tbody]: (
|
|
node: RTEDefaultNode,
|
|
embeds: EmbedByUid,
|
|
next: RTENext,
|
|
fullRenderOptions: RenderOptions
|
|
) => {
|
|
const props = extractPossibleAttributes(node.attrs)
|
|
return (
|
|
<Table.TBody key={node.uid} {...props}>
|
|
{next(node.children, embeds, fullRenderOptions)}
|
|
</Table.TBody>
|
|
)
|
|
},
|
|
|
|
[RTETypeEnum.tfoot]: (
|
|
node: RTEDefaultNode,
|
|
embeds: EmbedByUid,
|
|
next: RTENext,
|
|
fullRenderOptions: RenderOptions
|
|
) => {
|
|
const props = extractPossibleAttributes(node.attrs)
|
|
return (
|
|
<tfoot key={node.uid} {...props}>
|
|
{next(node.children, embeds, fullRenderOptions)}
|
|
</tfoot>
|
|
)
|
|
},
|
|
|
|
[RTETypeEnum.fragment]: (
|
|
node: RTEDefaultNode,
|
|
embeds: EmbedByUid,
|
|
next: RTENext,
|
|
fullRenderOptions: RenderOptions
|
|
) => {
|
|
return <>{next(node.children, embeds, fullRenderOptions)}</>
|
|
},
|
|
|
|
[RTETypeEnum.tr]: (
|
|
node: RTEDefaultNode,
|
|
embeds: EmbedByUid,
|
|
next: RTENext,
|
|
fullRenderOptions: RenderOptions
|
|
) => {
|
|
const props = extractPossibleAttributes(node.attrs)
|
|
return (
|
|
<Table.TR key={node.uid} {...props}>
|
|
{next(node.children, embeds, fullRenderOptions)}
|
|
</Table.TR>
|
|
)
|
|
},
|
|
|
|
[RTETypeEnum.th]: (
|
|
node: RTEDefaultNode,
|
|
embeds: EmbedByUid,
|
|
next: RTENext,
|
|
fullRenderOptions: RenderOptions
|
|
) => {
|
|
const props = extractPossibleAttributes(node.attrs)
|
|
return (
|
|
<Table.TH key={node.uid} {...props}>
|
|
{next(node.children, embeds, fullRenderOptions)}
|
|
</Table.TH>
|
|
)
|
|
},
|
|
|
|
[RTETypeEnum.td]: (
|
|
node: RTEDefaultNode,
|
|
embeds: EmbedByUid,
|
|
next: RTENext,
|
|
fullRenderOptions: RenderOptions
|
|
) => {
|
|
const props = extractPossibleAttributes(node.attrs)
|
|
return (
|
|
<Table.TD key={node.uid} {...props}>
|
|
{next(node.children, embeds, fullRenderOptions)}
|
|
</Table.TD>
|
|
)
|
|
},
|
|
|
|
[RTETypeEnum.ul]: (
|
|
node: RTEDefaultNode,
|
|
embeds: EmbedByUid,
|
|
next: RTENext,
|
|
fullRenderOptions: RenderOptions
|
|
) => {
|
|
const props = extractPossibleAttributes(node.attrs)
|
|
const compatibleClassName = makeCssModuleCompatibleClassName(
|
|
props.className,
|
|
"ul"
|
|
)
|
|
|
|
// Set the number of rows dynamically to create even rows for each column. We want the li:s
|
|
// to flow with the column, so therefore this is needed.
|
|
let numberOfRows: number | undefined
|
|
if (node.children.length > 4) {
|
|
const half = node.children.length / 2
|
|
numberOfRows = Math.ceil(half)
|
|
}
|
|
|
|
return (
|
|
<ul
|
|
key={node.uid}
|
|
{...props}
|
|
className={`${styles.ul} ${compatibleClassName}`}
|
|
style={
|
|
numberOfRows
|
|
? {
|
|
gridTemplateRows: `repeat(${numberOfRows}, auto)`,
|
|
}
|
|
: {}
|
|
}
|
|
>
|
|
{next(node.children, embeds, fullRenderOptions)}
|
|
</ul>
|
|
)
|
|
},
|
|
|
|
/** TextNode wrappers */
|
|
[RTEMarkType.bold]: (children: React.ReactNode) => {
|
|
return <strong>{children}</strong>
|
|
},
|
|
|
|
[RTEMarkType.italic]: (children: React.ReactNode) => {
|
|
return <em>{children}</em>
|
|
},
|
|
|
|
[RTEMarkType.underline]: (children: React.ReactNode) => {
|
|
return <u>{children}</u>
|
|
},
|
|
|
|
[RTEMarkType.strikethrough]: (children: React.ReactNode) => {
|
|
return <s>{children}</s>
|
|
},
|
|
|
|
[RTEMarkType.inlineCode]: (children: React.ReactNode) => {
|
|
return <span>{children}</span>
|
|
},
|
|
|
|
[RTEMarkType.subscript]: (children: React.ReactNode) => {
|
|
return <sub>{children}</sub>
|
|
},
|
|
|
|
[RTEMarkType.superscript]: (children: React.ReactNode) => {
|
|
return <sup>{children}</sup>
|
|
},
|
|
|
|
[RTEMarkType.break]: (children: React.ReactNode) => {
|
|
return (
|
|
<>
|
|
<br />
|
|
{children}
|
|
</>
|
|
)
|
|
},
|
|
|
|
[RTEMarkType.classnameOrId]: (
|
|
children: React.ReactNode,
|
|
className?: string,
|
|
id?: string
|
|
) => {
|
|
let props = {
|
|
className,
|
|
id,
|
|
}
|
|
if (!className) {
|
|
delete props.className
|
|
}
|
|
if (!id) {
|
|
delete props.id
|
|
}
|
|
|
|
if (className) {
|
|
if (hasAvailableULFormat(className)) {
|
|
// @ts-ignore: We want to set css modules classNames even if it does not correspond
|
|
// to an existing class in the module style sheet. Due to our css modules plugin for
|
|
// typescript, we cannot do this without the ts-ignore
|
|
props.className = styles[className]
|
|
}
|
|
}
|
|
|
|
if (className === AvailableParagraphFormatEnum.footnote) {
|
|
return (
|
|
<Footnote key={id} {...props}>
|
|
{children}
|
|
</Footnote>
|
|
)
|
|
}
|
|
|
|
if (className === AvailableParagraphFormatEnum.caption) {
|
|
return (
|
|
<Caption key={id} {...props}>
|
|
{children}
|
|
</Caption>
|
|
)
|
|
}
|
|
|
|
if (className === AvailableParagraphFormatEnum["script-1"]) {
|
|
return (
|
|
<BiroScript key={id} type="one" {...props}>
|
|
{children}
|
|
</BiroScript>
|
|
)
|
|
}
|
|
|
|
if (className === AvailableParagraphFormatEnum["script-2"]) {
|
|
return (
|
|
<BiroScript key={id} type="two" {...props}>
|
|
{children}
|
|
</BiroScript>
|
|
)
|
|
}
|
|
|
|
if (className === AvailableParagraphFormatEnum["subtitle-1"]) {
|
|
return (
|
|
<Subtitle key={id} {...props} type="one">
|
|
{children}
|
|
</Subtitle>
|
|
)
|
|
}
|
|
if (className === AvailableParagraphFormatEnum["subtitle-2"]) {
|
|
return (
|
|
<Subtitle key={id} {...props} type="two">
|
|
{children}
|
|
</Subtitle>
|
|
)
|
|
}
|
|
return (
|
|
<span key={id} {...props}>
|
|
{children}
|
|
</span>
|
|
)
|
|
},
|
|
|
|
/**
|
|
* Contentstack can return something called `default` as seen here in their
|
|
* own SDK (https://github.com/contentstack/contentstack-utils-javascript/blob/master/src/options/default-node-options.ts#L89)
|
|
*/
|
|
default: (
|
|
node: RTEDefaultNode,
|
|
embeds: EmbedByUid,
|
|
next: RTENext,
|
|
fullRenderOptions: RenderOptions
|
|
) => {
|
|
return next(node.children, embeds, fullRenderOptions)
|
|
},
|
|
}
|