diff --git a/components/Current/Aside/Puff/index.tsx b/components/Current/Aside/Puff/index.tsx index f435c442d..a08f99605 100644 --- a/components/Current/Aside/Puff/index.tsx +++ b/components/Current/Aside/Puff/index.tsx @@ -6,6 +6,7 @@ import { Button } from "@scandic-hotels/design-system/current" import Image from "@/components/Image" import JsonToHtml from "@/components/JsonToHtml" +import { renderOptions as currentRenderOption } from "./../../currentRenderOptions" import { renderOptions } from "./renderOptions" import styles from "./puff.module.css" @@ -44,7 +45,7 @@ export default function Puff({
diff --git a/components/Current/Blocks/Text.tsx b/components/Current/Blocks/Text.tsx index d79bf1518..4d20bd1fc 100644 --- a/components/Current/Blocks/Text.tsx +++ b/components/Current/Blocks/Text.tsx @@ -1,5 +1,7 @@ import JsonToHtml from "@/components/JsonToHtml" +import { renderOptions } from "./../currentRenderOptions" + import type { TextProps } from "@/types/components/current/blocks/text" export default function Text({ text }: TextProps) { @@ -7,6 +9,7 @@ export default function Text({ text }: TextProps) { ) } diff --git a/components/Current/Preamble/index.tsx b/components/Current/Preamble/index.tsx index d615a6da6..ef0dc682a 100644 --- a/components/Current/Preamble/index.tsx +++ b/components/Current/Preamble/index.tsx @@ -1,5 +1,6 @@ import JsonToHtml from "@/components/JsonToHtml" +import { renderOptions as currentRenderOption } from "./../currentRenderOptions" import Breadcrumbs from "./Breadcrumbs" import { renderOptions } from "./renderOptions" @@ -27,7 +28,7 @@ export default function Preamble({ ) : null} diff --git a/components/Current/currentRenderOptions.module.css b/components/Current/currentRenderOptions.module.css new file mode 100644 index 000000000..2e564ffac --- /dev/null +++ b/components/Current/currentRenderOptions.module.css @@ -0,0 +1,6 @@ +.image { + height: auto; + margin-bottom: var(--Spacing-x2); + max-width: 100%; + object-fit: cover; +} diff --git a/components/Current/currentRenderOptions.tsx b/components/Current/currentRenderOptions.tsx new file mode 100644 index 000000000..ae8c465be --- /dev/null +++ b/components/Current/currentRenderOptions.tsx @@ -0,0 +1,474 @@ +import Image from "@/components/Image" +import Link from "@/components/TempDesignSystem/Link" + +import styles from "./currentRenderOptions.module.css" + +import type { EmbedByUid } from "@/types/components/jsontohtml" +import { EmbedEnum } from "@/types/requests/utils/embeds" +import type { Attributes } from "@/types/rte/attrs" +import { RTEItemTypeEnum, RTETypeEnum } from "@/types/rte/enums" +import type { + RTEDefaultNode, + RTENext, + RTENode, + RTERegularNode, +} from "@/types/rte/node" +import { RTEMarkType } from "@/types/rte/node" +import type { RenderOptions } from "@/types/rte/option" + +function extractPossibleAttributes(attrs: Attributes | undefined) { + if (!attrs) return {} + const props: Record = {} + 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 + } else 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 ( + + {next(node.children, embeds, fullRenderOptions)} + + ) + } + return null + }, + + [RTETypeEnum.blockquote]: ( + node: RTEDefaultNode, + embeds: EmbedByUid, + next: RTENext, + fullRenderOptions: RenderOptions + ) => { + const props = extractPossibleAttributes(node.attrs) + return ( +
+ {next(node.children, embeds, fullRenderOptions)} +
+ ) + }, + + [RTETypeEnum.code]: ( + node: RTEDefaultNode, + embeds: EmbedByUid, + next: RTENext, + fullRenderOptions: RenderOptions + ) => { + const props = extractPossibleAttributes(node.attrs) + return ( + + {next(node.children, embeds, fullRenderOptions)} + + ) + }, + + [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 ( + + ) + }, + + [RTETypeEnum.h1]: ( + node: RTEDefaultNode, + embeds: EmbedByUid, + next: RTENext, + fullRenderOptions: RenderOptions + ) => { + const props = extractPossibleAttributes(node.attrs) + return ( +

+ {next(node.children, embeds, fullRenderOptions)} +

+ ) + }, + + [RTETypeEnum.h2]: ( + node: RTEDefaultNode, + embeds: EmbedByUid, + next: RTENext, + fullRenderOptions: RenderOptions + ) => { + const props = extractPossibleAttributes(node.attrs) + return ( +

+ {next(node.children, embeds, fullRenderOptions)} +

+ ) + }, + + [RTETypeEnum.h3]: ( + node: RTEDefaultNode, + embeds: EmbedByUid, + next: RTENext, + fullRenderOptions: RenderOptions + ) => { + const props = extractPossibleAttributes(node.attrs) + return ( +

+ {next(node.children, embeds, fullRenderOptions)} +

+ ) + }, + + [RTETypeEnum.h4]: ( + node: RTEDefaultNode, + embeds: EmbedByUid, + next: RTENext, + fullRenderOptions: RenderOptions + ) => { + const props = extractPossibleAttributes(node.attrs) + return ( +

+ {next(node.children, embeds, fullRenderOptions)} +

+ ) + }, + + [RTETypeEnum.h5]: ( + node: RTEDefaultNode, + embeds: EmbedByUid, + next: RTENext, + fullRenderOptions: RenderOptions + ) => { + const props = extractPossibleAttributes(node.attrs) + return ( +
+ {next(node.children, embeds, fullRenderOptions)} +
+ ) + }, + + [RTETypeEnum.h6]: ( + node: RTEDefaultNode, + embeds: EmbedByUid, + next: RTENext, + fullRenderOptions: RenderOptions + ) => { + const props = extractPossibleAttributes(node.attrs) + return ( +
+ {next(node.children, embeds, fullRenderOptions)} +
+ ) + }, + + [RTETypeEnum.hr]: () => { + return
+ }, + + [RTETypeEnum.li]: ( + node: RTEDefaultNode, + embeds: EmbedByUid, + next: RTENext, + fullRenderOptions: RenderOptions + ) => { + const props = extractPossibleAttributes(node.attrs) + return ( +
  • + {next(node.children, embeds, fullRenderOptions)} +
  • + ) + }, + + [RTETypeEnum.ol]: ( + node: RTEDefaultNode, + embeds: EmbedByUid, + next: RTENext, + fullRenderOptions: RenderOptions + ) => { + const props = extractPossibleAttributes(node.attrs) + return ( +
      + {next(node.children, embeds, fullRenderOptions)} +
    + ) + }, + + [RTETypeEnum.p]: ( + node: RTEDefaultNode, + embeds: EmbedByUid, + next: RTENext, + fullRenderOptions: RenderOptions + ) => { + const props = extractPossibleAttributes(node.attrs) + return ( +

    + {next(node.children, embeds, fullRenderOptions)} +

    + ) + }, + + [RTETypeEnum.reference]: ( + node: RTENode, + embeds: EmbedByUid, + next: RTENext, + fullRenderOptions: RenderOptions + ) => { + if ("attrs" in node) { + const type = node.attrs.type + if (type === RTEItemTypeEnum.asset) { + const image = embeds?.[node?.attrs?.["asset-uid"]] + if (image?.node.__typename === EmbedEnum.SysAsset) { + const alt = image?.node?.title ?? node.attrs.alt + const alignment = node.attrs?.style?.["text-align"] + ? { + alignSelf: node.attrs?.style?.["text-align"], + } + : {} + return ( + {alt} + ) + } + } else { + const props = extractPossibleAttributes(node.attrs) + const href = node.attrs?.locale + ? `/${node.attrs.locale}${node.attrs.href}` + : node.attrs.href + return ( + + {next(node.children, embeds, fullRenderOptions)} + + ) + } + } + + return null + }, + + [RTETypeEnum.table]: ( + node: RTEDefaultNode, + embeds: EmbedByUid, + next: RTENext, + fullRenderOptions: RenderOptions + ) => { + const props = extractPossibleAttributes(node.attrs) + return ( + + {next(node.children, embeds, fullRenderOptions)} +
    + ) + }, + + [RTETypeEnum.thead]: ( + node: RTEDefaultNode, + embeds: EmbedByUid, + next: RTENext, + fullRenderOptions: RenderOptions + ) => { + const props = extractPossibleAttributes(node.attrs) + return ( + + {next(node.children, embeds, fullRenderOptions)} + + ) + }, + + [RTETypeEnum.tbody]: ( + node: RTEDefaultNode, + embeds: EmbedByUid, + next: RTENext, + fullRenderOptions: RenderOptions + ) => { + const props = extractPossibleAttributes(node.attrs) + return ( + + {next(node.children, embeds, fullRenderOptions)} + + ) + }, + + [RTETypeEnum.tfoot]: ( + node: RTEDefaultNode, + embeds: EmbedByUid, + next: RTENext, + fullRenderOptions: RenderOptions + ) => { + const props = extractPossibleAttributes(node.attrs) + return ( + + {next(node.children, embeds, fullRenderOptions)} + + ) + }, + + [RTETypeEnum.tr]: ( + node: RTEDefaultNode, + embeds: EmbedByUid, + next: RTENext, + fullRenderOptions: RenderOptions + ) => { + const props = extractPossibleAttributes(node.attrs) + return ( + + {next(node.children, embeds, fullRenderOptions)} + + ) + }, + + [RTETypeEnum.th]: ( + node: RTEDefaultNode, + embeds: EmbedByUid, + next: RTENext, + fullRenderOptions: RenderOptions + ) => { + const props = extractPossibleAttributes(node.attrs) + return ( + + {next(node.children, embeds, fullRenderOptions)} + + ) + }, + + [RTETypeEnum.td]: ( + node: RTEDefaultNode, + embeds: EmbedByUid, + next: RTENext, + fullRenderOptions: RenderOptions + ) => { + const props = extractPossibleAttributes(node.attrs) + return ( + + {next(node.children, embeds, fullRenderOptions)} + + ) + }, + + [RTETypeEnum.ul]: ( + node: RTEDefaultNode, + embeds: EmbedByUid, + next: RTENext, + fullRenderOptions: RenderOptions + ) => { + const props = extractPossibleAttributes(node.attrs) + return ( +
      + {next(node.children, embeds, fullRenderOptions)} +
    + ) + }, + + /** TextNode wrappers */ + [RTEMarkType.bold]: (children: React.ReactNode) => { + return {children} + }, + + [RTEMarkType.italic]: (children: React.ReactNode) => { + return {children} + }, + + [RTEMarkType.underline]: (children: React.ReactNode) => { + return {children} + }, + + [RTEMarkType.strikethrough]: (children: React.ReactNode) => { + return {children} + }, + + [RTEMarkType.inlineCode]: (children: React.ReactNode) => { + return {children} + }, + + [RTEMarkType.subscript]: (children: React.ReactNode) => { + return {children} + }, + + [RTEMarkType.superscript]: (children: React.ReactNode) => { + return {children} + }, + + [RTEMarkType.break]: (children: React.ReactNode) => { + return ( + <> +
    + {children} + + ) + }, + + [RTEMarkType.classnameOrId]: ( + children: React.ReactNode, + className?: string, + id?: string + ) => { + let props = { + className, + id, + } + if (!className) { + delete props.className + } + if (!id) { + delete props.id + } + return ( + + {children} + + ) + }, + + /** + * 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) + }, +} diff --git a/components/JsonToHtml/index.tsx b/components/JsonToHtml/index.tsx index 9beebccae..689bd0460 100644 --- a/components/JsonToHtml/index.tsx +++ b/components/JsonToHtml/index.tsx @@ -10,5 +10,10 @@ export default function JsonToHtml({ if (!Array.isArray(nodes) || !nodes.length) { return null } - return <>{nodesToHtml(nodes, embeds, renderOptions).filter(Boolean)} + console.log({ nodes }) + return ( +
    + {nodesToHtml(nodes, embeds, renderOptions).filter(Boolean)} +
    + ) } diff --git a/components/JsonToHtml/jsontohtml.module.css b/components/JsonToHtml/jsontohtml.module.css index 2e564ffac..178802905 100644 --- a/components/JsonToHtml/jsontohtml.module.css +++ b/components/JsonToHtml/jsontohtml.module.css @@ -1,6 +1,8 @@ .image { - height: auto; - margin-bottom: var(--Spacing-x2); max-width: 100%; + width: 100%; + height: 365px; object-fit: cover; + border-radius: var(--Corner-radius-Medium); + padding: var(--Spacing-x1) var(--Spacing-x0); } diff --git a/components/JsonToHtml/renderOptions.tsx b/components/JsonToHtml/renderOptions.tsx index 1b745815e..cd748e58e 100644 --- a/components/JsonToHtml/renderOptions.tsx +++ b/components/JsonToHtml/renderOptions.tsx @@ -1,14 +1,23 @@ import Image from "@/components/Image" import Link from "@/components/TempDesignSystem/Link" +import { insertResponseToImageVaultAsset } from "@/utils/imageVault" + +import Divider from "../TempDesignSystem/Divider" +import BiroScript from "../TempDesignSystem/Text/BiroScript" +import Body from "../TempDesignSystem/Text/Body" +import Caption from "../TempDesignSystem/Text/Caption" +import Subtitle from "../TempDesignSystem/Text/Subtitle" +import Title from "../TempDesignSystem/Text/Title" import styles from "./jsontohtml.module.css" import type { EmbedByUid } from "@/types/components/jsontohtml" import { EmbedEnum } from "@/types/requests/utils/embeds" -import type { Attributes } from "@/types/rte/attrs" +import type { Attributes, RTEImageVaultAttrs } from "@/types/rte/attrs" import { RTEItemTypeEnum, RTETypeEnum } from "@/types/rte/enums" import type { RTEDefaultNode, + RTEImageNode, RTENext, RTENode, RTERegularNode, @@ -69,9 +78,9 @@ export const renderOptions: RenderOptions = { ) => { const props = extractPossibleAttributes(node.attrs) return ( -
    + {next(node.children, embeds, fullRenderOptions)} -
    + ) }, @@ -120,9 +129,9 @@ export const renderOptions: RenderOptions = { ) => { const props = extractPossibleAttributes(node.attrs) return ( -

    + {next(node.children, embeds, fullRenderOptions)} - </h1> + ) }, @@ -134,9 +143,9 @@ export const renderOptions: RenderOptions = { ) => { const props = extractPossibleAttributes(node.attrs) return ( -

    + {next(node.children, embeds, fullRenderOptions)} - </h2> + ) }, @@ -148,9 +157,9 @@ export const renderOptions: RenderOptions = { ) => { const props = extractPossibleAttributes(node.attrs) return ( -

    + {next(node.children, embeds, fullRenderOptions)} - </h3> + ) }, @@ -162,9 +171,9 @@ export const renderOptions: RenderOptions = { ) => { const props = extractPossibleAttributes(node.attrs) return ( -

    + {next(node.children, embeds, fullRenderOptions)} - </h4> + ) }, @@ -176,9 +185,9 @@ export const renderOptions: RenderOptions = { ) => { const props = extractPossibleAttributes(node.attrs) return ( -

    + {next(node.children, embeds, fullRenderOptions)} - </h5> + ) }, @@ -190,14 +199,14 @@ export const renderOptions: RenderOptions = { ) => { const props = extractPossibleAttributes(node.attrs) return ( -
    - {next(node.children, embeds, fullRenderOptions)} -
    + +
    {next(node.children, embeds, fullRenderOptions)}
    +
    ) }, [RTETypeEnum.hr]: () => { - return
    + return }, [RTETypeEnum.li]: ( @@ -236,9 +245,9 @@ export const renderOptions: RenderOptions = { ) => { const props = extractPossibleAttributes(node.attrs) return ( -

    + {next(node.children, embeds, fullRenderOptions)} -

    + ) }, @@ -254,11 +263,7 @@ export const renderOptions: RenderOptions = { const image = embeds?.[node?.attrs?.["asset-uid"]] if (image?.node.__typename === EmbedEnum.SysAsset) { const alt = image?.node?.title ?? node.attrs.alt - const alignment = node.attrs?.style?.["text-align"] - ? { - alignSelf: node.attrs?.style?.["text-align"], - } - : {} + const props = extractPossibleAttributes(node.attrs) return ( ) } @@ -287,6 +292,36 @@ export const renderOptions: RenderOptions = { return null }, + [RTETypeEnum.ImageVault]: (node: RTEImageNode) => { + if ("attrs" in node) { + const type = node.type + if (type === RTETypeEnum.ImageVault) { + const attrs = node.attrs as RTEImageVaultAttrs + const image = insertResponseToImageVaultAsset(attrs) + const alt = image.meta.alt ?? image.title + + const height = parseInt(attrs.height.replaceAll("px", "")) + const width = parseInt(attrs.width.replaceAll("px", "")) + const props = extractPossibleAttributes(attrs) + return ( +
    + {alt} + {image.meta.caption} +
    + ) + } + } + + return null + }, + [RTETypeEnum.table]: ( node: RTEDefaultNode, embeds: EmbedByUid, diff --git a/types/rte/attrs.ts b/types/rte/attrs.ts index f8bb36e30..dd658872d 100644 --- a/types/rte/attrs.ts +++ b/types/rte/attrs.ts @@ -1,6 +1,8 @@ +import { InsertResponse } from "../components/imageVaultImage" import { RTEItemTypeEnum } from "./enums" -import type { EmbedTypesEnum, RTEItemType } from "./enums" + import type { Lang } from "@/constants/languages" +import type { EmbedTypesEnum, RTEItemType } from "./enums" export interface Attributes { [key: string]: any @@ -36,3 +38,9 @@ export interface RTELinkAttrs extends Attributes { target: HTMLAnchorElement["target"] type: RTEItemTypeEnum.entry } + +export interface RTEImageVaultAttrs extends Attributes, InsertResponse { + height: string + width: string + style: string[] +} diff --git a/types/rte/enums.ts b/types/rte/enums.ts index b5eb2a6a1..271c800aa 100644 --- a/types/rte/enums.ts +++ b/types/rte/enums.ts @@ -36,6 +36,7 @@ export enum RTETypeEnum { thead = "thead", tr = "tr", ul = "ul", + ImageVault = "ImageVault", } export type RTEType = keyof typeof RTETypeEnum diff --git a/types/rte/node.ts b/types/rte/node.ts index c762f33bb..dca0f9281 100644 --- a/types/rte/node.ts +++ b/types/rte/node.ts @@ -5,6 +5,7 @@ import type { Attributes, RTEAnchorAttrs, RTEAssetAttrs, + RTEImageVaultAttrs, RTELinkAttrs, } from "./attrs" import type { RenderOptions } from "./option" @@ -36,6 +37,11 @@ export interface RTEReferenceLinkNode extends RTEDefaultNode { attrs: RTELinkAttrs } +export interface RTEImageVaultNode extends RTEDefaultNode { + attrs: RTEImageVaultAttrs + type: RTETypeEnum.ImageVault +} + export enum RTEMarkType { bold = "bold", break = "break", @@ -58,9 +64,11 @@ export type RTETextNode = RTETextNodeOptionalKeys & { text: string } -export type RTERegularNode = RTEDefaultNode | RTEAnchorNode +export type RTERegularNode = RTEDefaultNode | RTEAnchorNode | RTEImageVaultNode -export type RTEReferenceNode = RTEDefaultNode | RTEAnchorNode +export type RTEImageNode = RTEDefaultNode | RTEImageVaultNode + +export type RTEReferenceNode = RTEAnchorNode export type RTENode = RTERegularNode | RTEReferenceNode | RTETextNode