Skip to content

Commit

Permalink
feat: add support for Liquid canvas blocks (#22)
Browse files Browse the repository at this point in the history
feat: added default forms render

feat: updated rendering of forms within canvas and ensured canvas renderer can handle unknown types

feat: added liquid support to canvas

---------

Co-authored-by: Dan White <[email protected]>
  • Loading branch information
nflatley-zengenti and dantkid authored Sep 26, 2024
1 parent c9d087f commit c18c591
Show file tree
Hide file tree
Showing 29 changed files with 546 additions and 109 deletions.
174 changes: 174 additions & 0 deletions apps/node/content/canvas-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -713,4 +713,178 @@ export const data: Block[] = [
},
"value": "I feel the need, the need for speed"
}
];

export const emailData: Block[] = [
{
"id": "1f7b06d4",
"type": "_divider"
},
{
"id": "db6fbe71",
"type": "_heading",
"properties": {
"level": 1
},
"value": "Heading one"
},
{
"id": "3d4ca613",
"type": "_heading",
"properties": {
"level": 2
},
"value": "Heading two"
},
{
"id": "fe7a61db",
"type": "_paragraph",
"value": "Fringilla vivamus facilisi est sed mi cubilia fusce penatibus. Praesent enim id molestie suspendisse arcu luctus. Quis pede praesent etiam cras nunc molestie massa consequat mi fringilla. Consectetur dictumst pellentesque maecenas quis adipiscing efficitur ad dis gravida. Porta venenatis pretium magna dictumst nullam placerat proin elit posuere ligula amet. Penatibus blandit ad egestas duis nascetur suscipit in vivamus."
},
{
"id": "17184e46",
"type": "_list",
"properties": {
"listType": "ordered"
},
"value": [
{
"id": "f85f8d21",
"type": "_listItem",
"value": "Consectetur dictumst pellentesque maecenas quis adipiscing efficitur ad dis gravida."
},
{
"id": "a29cc5c8",
"type": "_listItem",
"value": "Porta venenatis pretium magna dictumst nullam placerat proin elit posuere ligula amet."
},
{
"id": "f5cec1e6",
"type": "_listItem",
"value": "Penatibus blandit ad egestas duis nascetur suscipit in vivamus."
}
]
},
{
"id": "f58fad3c",
"type": "_paragraph",
"value": "Himenaeos et sagittis sociosqu proin ex ligula interdum condimentum porttitor. Potenti turpis vestibulum consectetuer amet laoreet dictum justo dolor. Enim euismod elementum commodo lacinia viverra. Leo montes augue facilisi massa bibendum rhoncus lobortis interdum. Ornare risus taciti adipiscing ultrices metus imperdiet laoreet condimentum. Placerat lacinia semper condimentum dictum dolor. In accumsan curae at massa mollis potenti hendrerit habitasse hac. Facilisi risus urna vitae ante auctor ultricies sagittis turpis semper."
},
{
"id": "96363391",
"type": "_list",
"properties": {
"listType": "unordered"
},
"value": [
{
"id": "ea27614d",
"type": "_listItem",
"value": "Consectetur dictumst pellentesque maecenas quis adipiscing efficitur ad dis gravida."
},
{
"id": "744a890f",
"type": "_listItem",
"value": "Porta venenatis pretium magna dictumst nullam placerat proin elit posuere ligula amet."
},
{
"id": "8a454e21",
"type": "_listItem",
"value": "Penatibus blandit ad egestas duis nascetur suscipit in vivamus."
}
]
},
{
"id": "22116229",
"type": "_heading",
"properties": {
"level": 3
},
"value": "Heading three"
},
{
"id": "61a42305",
"type": "_table",
"value": [
{
"id": "92a840c2",
"type": "_tableCaption",
"value": []
},
{
"id": "35eb22dc",
"type": "_tableBody",
"value": [
{
"id": "2b74719",
"type": "_tableRow",
"value": [
{
"id": "44ee4eb5",
"type": "_tableHeaderCell",
"value": "Name:"
},
{
"id": "4d9bba8b",
"type": "_tableCell",
"value": "{{name}}"
}
]
},
{
"id": "1a301c54",
"type": "_tableRow",
"value": [
{
"id": "d25b6aa5",
"type": "_tableCell",
"value": "Email:"
},
{
"id": "bb68de3e",
"type": "_tableCell",
"value": "{{email}}"
}
]
},
{
"id": "fe1a1578",
"type": "_tableRow",
"value": [
{
"id": "1be2e16a",
"type": "_tableCell",
"value": "Phone:"
},
{
"id": "cabab943",
"type": "_tableCell",
"value": "{{phoneNumber}}"
}
]
},
{
"id": "2978b392",
"type": "_tableRow",
"value": [
{
"id": "c077dfed",
"type": "_tableCell",
"value": "Message:"
},
{
"id": "7c56ceec",
"type": "_tableCell",
"value": "{{message}}"
}
]
}
]
}
]
},
{
"id": "3f36dbd9",
"type": "_divider"
}
];
40 changes: 40 additions & 0 deletions apps/node/content/contents.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createRenderer } from '@contensis/canvas-html';
import { myHtmlHeading, myHtmlParagraph, myHtmlFragment, myHtmlTable, myHtmlPanel, myHtmlImage, myHtmlCode, myHtmlList, myHtmlListItem, myHtmlAuthorComponent, myHtmlBookComponent } from './elements';
import { Block } from '@contensis/canvas-types';
import { divider, heading, table, tableCell, tableHeaderCell } from '@contensis/canvas-html';

const renderer = createRenderer({
blocks: {
Expand All @@ -23,3 +24,42 @@ const renderer = createRenderer({
export function getHtml(data: Block[]) {
return renderer({ data });
}


const emailRenderer = createRenderer({
blocks: {
_divider: function (props: any) {
return divider({ ...props, style: 'background-color: #dbdbdb; border: 0; height: 1px; margin: 32px 0' });
},
_heading: function (props: any) {
const level = props.block?.properties?.level || 1;
switch (level) {
case 1: {
return heading({ ...props, style: 'font-size: 18px; line-height: 24px; font-weight: 800; margin-bottom: 16px;' })
}
case 2: {
return heading({ ...props, style: 'font-size: 16px; line-height: 24px; margin-bottom: 16px' });
}
case 3: {
return heading({ ...props, style: 'font-size: 14px; line-height: 20px; margin-bottom: 16px' });
}
default: {
return heading(props);
}
}
},
_table: function (props: any) {
return table({ ...props, style: 'margin-top: 8px; margin-bottom: 32px' });
},
_tableCell: function (props: any) {
return tableCell({ ...props, style: 'padding: 8px 0' });
},
_tableHeaderCell: function (props: any) {
return tableHeaderCell({ ...props, style: 'padding: 8px 0' });
}
}
});

export function toEmail(data: Block[]) {
return emailRenderer({ data });
}
6 changes: 5 additions & 1 deletion apps/node/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import express, { Request, Response } from 'express';
import * as CanvasData from './content/canvas-data';
import { getHtml } from './content/contents';
import { toEmail, getHtml } from './content/contents';

const app = express();

Expand All @@ -10,4 +10,8 @@ app.get('/', (req: Request, res: Response) => {
res.render('pages/index', { content: getHtml(CanvasData.data) });
});

app.get('/email', (req: Request, res: Response) => {
res.render('pages/plain', { content: toEmail(CanvasData.emailData) });
});

export const viteNodeApp = app;
19 changes: 19 additions & 0 deletions apps/node/views/pages/plain.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Node - EJS</title>
<style>
div.content {
margin: auto;
width: 75%;
}
</style>
</head>

<body>
<div class="content"><%- content %></div>
</body>
</html>
37 changes: 20 additions & 17 deletions packages/dom/src/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
ImageBlock,
InlineEntryBlock,
LinkBlock,
LiquidBlock,
ListBlock,
ListItemBlock,
PanelBlock,
Expand Down Expand Up @@ -49,14 +50,8 @@ type WithRenderers<TNode, TFragment> = {

type RendererProps = { data: Block[]; context?: RenderContext };
type RenderBlocksProps<TNode, TFragment> = { blocks: Block[] } & WithContext & WithH<TNode, TFragment> & WithRenderers<TNode, TFragment>;
type RenderBlockProps<T extends Block, TNode, TFragment> = { block: T } & WithContext &
Attributes &
WithH<TNode, TFragment> &
WithRenderers<TNode, TFragment>;
type RenderDecoratorProps<TNode, TFragment> = { block: FragmentBlock; decorator: DecoratorType; otherDecorators: DecoratorType[] } & WithH<
TNode,
TFragment
> &
type RenderBlockProps<T extends Block, TNode, TFragment> = { block: T } & WithContext & Attributes & WithH<TNode, TFragment> & WithRenderers<TNode, TFragment>;
type RenderDecoratorProps<TNode, TFragment> = { block: FragmentBlock; decorator: DecoratorType; otherDecorators: DecoratorType[] } & WithH<TNode, TFragment> &
WithRenderers<TNode, TFragment> &
WithContext &
Attributes;
Expand All @@ -68,7 +63,7 @@ type TypedBlock<TType extends Block['type']> = Extract<Block, { type: TType }>;

type BlockRenderer<T extends Block, TNode, TFragment> = (props: RenderBlockProps<T, TNode, TFragment>, ...children: TNode[]) => TNode;
type BlockRenderers<TNode, TFragment> = {
[TType in Block['type']]: BlockRenderer<TypedBlock<TType>, TNode, TFragment>
[TType in Block['type']]: BlockRenderer<TypedBlock<TType>, TNode, TFragment>;
};

type DecoratorRenderer<TNode, TFragment> = (props: RenderDecoratorProps<TNode, TFragment>, ...children: TNode[]) => TNode;
Expand Down Expand Up @@ -300,7 +295,6 @@ divider.children = function <TNode, TFragment>(_props: RenderBlockProps<DividerB
return null;
};


function formContentType<TNode, TFragment>(props: RenderBlockProps<FormContentTypeBlock, TNode, TFragment>, ...children: TNode[]) {
const { block, context, renderers, h, hFragment, hText } = props;
const attributes = getAttributes(props, {
Expand All @@ -314,7 +308,6 @@ formContentType.children = function <TNode, TFragment>(props: RenderBlockProps<F
return toFragment(props, [hText(`Form: ${props.block?.value?.contentType?.id}`)]);
};


function fragment<TNode, TFragment>(props: RenderBlockProps<FragmentBlock, TNode, TFragment>, ...children: TNode[]) {
const { block, context, renderers, h, hFragment, hText } = props;
const hasDecorators = !!block?.properties?.decorators?.length;
Expand Down Expand Up @@ -394,6 +387,17 @@ function link<TNode, TFragment>(props: RenderBlockProps<LinkBlock, TNode, TFragm

link.children = childRenderer<LinkBlock>();

function liquid<TNode, TFragment>(props: RenderBlockProps<LiquidBlock, TNode, TFragment>, ...children: TNode[]) {
const { block, context, decorator, otherDecorators, renderers, h, hFragment, hText } = props;
children = getChildren(children, () => liquid.children({ block, context, decorator, otherDecorators, renderers, h, hFragment, hText }));
return toFragment(props, children);
}

liquid.children = function <TNode, TFragment>(props: RenderBlockProps<LiquidBlock, TNode, TFragment>) {
const { hText } = props;
return toFragment(props, [hText(props.block?.value)]);
};

const list = createBlockRenderer<ListBlock>(
(block) => (block?.properties?.listType === 'ordered' ? 'ol' : 'ul'),
(block) => ({
Expand Down Expand Up @@ -484,6 +488,7 @@ function createRendererFactory<TNode, TFragment>(h: H<TNode, TFragment>, hFragme
_image: imageWithCaption,
_inlineEntry: inlineEntry,
_link: link,
_liquid: liquid,
_list: list,
_listItem: listItem,
_quote: quote,
Expand Down Expand Up @@ -561,6 +566,7 @@ function createElements<TNode, TFragment>() {
image: createBlockElement<ImageBlock, TNode, TFragment>(image),
inlineEntry: createBlockElement<InlineEntryBlock, TNode, TFragment>(inlineEntry),
link: createBlockElement<LinkBlock, TNode, TFragment>(link),
liquid: createBlockElement<LiquidBlock, TNode, TFragment>(liquid),
list: createBlockElement<ListBlock, TNode, TFragment>(list),
listItem: createBlockElement<ListItemBlock, TNode, TFragment>(listItem),
panel: createBlockElement<PanelBlock, TNode, TFragment>(panel),
Expand Down Expand Up @@ -593,19 +599,16 @@ function createElements<TNode, TFragment>() {

export type {
BlockRenderer,
BlockRendererWithChildren,
BlockRenderers,
BlockRendererWithChildren,
ComponentRenderer,
ComponentRenderers,
DecoratorRenderer,
DecoratorRendererWithChildren,
DecoratorRenderers,
DecoratorRendererWithChildren,
RenderBlockProps,
RenderDecoratorProps
};

export {
createElements,
createRendererFactory
};
export { createElements, createRendererFactory };

Loading

0 comments on commit c18c591

Please sign in to comment.