Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Comms console poc #134

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,23 @@ import {
VerticalAlignCenterOutlined,
VerticalAlignTopOutlined,
} from '@mui/icons-material';
import { Stack, ToggleButton } from '@mui/material';
import { Box, Button, Stack, ToggleButton } from '@mui/material';
import { ImageProps, ImagePropsSchema } from '@usewaypoint/block-image';

import BaseSidebarPanel from './helpers/BaseSidebarPanel';
import RadioGroupInput from './helpers/inputs/RadioGroupInput';
import TextDimensionInput from './helpers/inputs/TextDimensionInput';
import TextInput from './helpers/inputs/TextInput';
import MultiStylePropertyPanel from './helpers/style-inputs/MultiStylePropertyPanel';
import { addImage, useImages } from '../../../../documents/editor/EditorContext';

type ImageSidebarPanelProps = {
data: ImageProps;
setData: (v: ImageProps) => void;
};
export default function ImageSidebarPanel({ data, setData }: ImageSidebarPanelProps) {
const [, setErrors] = useState<Zod.ZodError | null>(null);
const images = useImages();

const updateData = (d: unknown) => {
const res = ImagePropsSchema.safeParse(d);
Expand All @@ -31,8 +33,39 @@ export default function ImageSidebarPanel({ data, setData }: ImageSidebarPanelPr
}
};

const handleFileUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (!file) {
return;
}

const reader = new FileReader();
reader.onload = () => {
addImage({ base64: reader.result as string, file });
};
reader.readAsDataURL(file);
};

const handleImageClick = (image: { base64: string; file: File }) => () => {
updateData({ ...data, props: { ...data.props, url: image.file.name } });
};

return (
<BaseSidebarPanel title="Image block">
<Button component="label">
Upload image
<input type="file" accept="image/*" hidden onChange={handleFileUpload} />
</Button>

<Box>
{images.map((image) => (
<Box onClick={handleImageClick(image)}>
<img key={image.file.name} src={image.base64} alt="Uploaded" style={{ width: 50 }} />
{image.file.name}
</Box>
))}
</Box>

<TextInput
label="Source URL"
defaultValue={data.props?.url ?? ''}
Expand Down
41 changes: 41 additions & 0 deletions packages/editor-sample/src/App/TemplatePanel/EmailPreview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React, { useMemo } from 'react';
import { useEffect, useRef } from 'react';
import { useImages } from '../../documents/editor/EditorContext';

interface EmailPreviewProps {
html: string;
}

export const EmailPreview = ({ html }: EmailPreviewProps) => {
const images = useImages();
const iframeRef = useRef<HTMLIFrameElement>(null);

const content = useMemo(() => {
let copy = html;
images.forEach((image) => {
copy = copy.replaceAll(image.file.name, image.base64);
})
return copy
}, [images]);

useEffect(() => {
if (iframeRef.current && content) {
const newChild = new DOMParser().parseFromString(content, 'text/html');
iframeRef.current?.contentDocument?.replaceChild(
newChild.documentElement,
iframeRef.current.contentDocument.documentElement
);
}
}, [iframeRef.current, content]);

return (
<iframe
ref={iframeRef}
style={{
width: '100%',
height: '100%',
}}
title="Email preview"
/>
);
};
9 changes: 6 additions & 3 deletions packages/editor-sample/src/App/TemplatePanel/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React from 'react';
import React, { useMemo } from 'react';

import { MonitorOutlined, PhoneIphoneOutlined } from '@mui/icons-material';
import { Box, Stack, SxProps, ToggleButton, ToggleButtonGroup, Tooltip } from '@mui/material';
import { Reader } from '@usewaypoint/email-builder';
import { Reader, renderToStaticMarkup } from '@usewaypoint/email-builder';

import EditorBlock from '../../documents/editor/EditorBlock';
import {
Expand All @@ -20,11 +20,13 @@ import ImportJson from './ImportJson';
import JsonPanel from './JsonPanel';
import MainTabsGroup from './MainTabsGroup';
import ShareButton from './ShareButton';
import { EmailPreview } from './EmailPreview';

export default function TemplatePanel() {
const document = useDocument();
const selectedMainTab = useSelectedMainTab();
const selectedScreenSize = useSelectedScreenSize();
const preview = useMemo(() => renderToStaticMarkup(document, { rootBlockId: 'root' }), [document])

let mainBoxSx: SxProps = {
height: '100%',
Expand Down Expand Up @@ -62,7 +64,8 @@ export default function TemplatePanel() {
case 'preview':
return (
<Box sx={mainBoxSx}>
<Reader document={document} rootBlockId="root" />
<EmailPreview html={preview}/>
{/* <Reader document={document} rootBlockId="root" /> */}
</Box>
);
case 'html':
Expand Down
17 changes: 17 additions & 0 deletions packages/editor-sample/src/documents/editor/EditorContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ import getConfiguration from '../../getConfiguration';

import { TEditorConfiguration } from './core';

export type FileWithBase64 = {
file: File;
base64: string;
};

type TValue = {
document: TEditorConfiguration;

Expand All @@ -14,6 +19,8 @@ type TValue = {

inspectorDrawerOpen: boolean;
samplesDrawerOpen: boolean;

images: Array<FileWithBase64>;
};

const editorStateStore = create<TValue>(() => ({
Expand All @@ -25,6 +32,7 @@ const editorStateStore = create<TValue>(() => ({

inspectorDrawerOpen: true,
samplesDrawerOpen: true,
images: [],
}));

export function useDocument() {
Expand Down Expand Up @@ -59,6 +67,10 @@ export function useSamplesDrawerOpen() {
return editorStateStore((s) => s.samplesDrawerOpen);
}

export function useImages() {
return editorStateStore((s) => s.images);
}

export function setSelectedBlockId(selectedBlockId: TValue['selectedBlockId']) {
const selectedSidebarTab = selectedBlockId === null ? 'styles' : 'block-configuration';
const options: Partial<TValue> = {};
Expand Down Expand Up @@ -107,3 +119,8 @@ export function toggleSamplesDrawerOpen() {
export function setSelectedScreenSize(selectedScreenSize: TValue['selectedScreenSize']) {
return editorStateStore.setState({ selectedScreenSize });
}

export const addImage = (image: FileWithBase64) => {
const images = editorStateStore.getState().images;
return editorStateStore.setState({ images: [...images, image] });
};
27 changes: 26 additions & 1 deletion packages/editor-sample/src/documents/editor/core.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@ import ContainerPropsSchema from '../blocks/Container/ContainerPropsSchema';
import EmailLayoutEditor from '../blocks/EmailLayout/EmailLayoutEditor';
import EmailLayoutPropsSchema from '../blocks/EmailLayout/EmailLayoutPropsSchema';
import EditorBlockWrapper from '../blocks/helpers/block-wrappers/EditorBlockWrapper';
import { useImages } from './EditorContext';

const CustomImagePropsSchema = ImagePropsSchema.extend({});

const isUrl = (value: string) => {
try {
new URL(value);
return true;
} catch {
return false;
}
};

const EDITOR_DICTIONARY = buildBlockConfigurationDictionary({
Avatar: {
Expand Down Expand Up @@ -75,13 +87,18 @@ const EDITOR_DICTIONARY = buildBlockConfigurationDictionary({
Image: {
schema: ImagePropsSchema,
Component: (data) => {
const images = useImages()
const props = {
...data,
props: {
...data.props,
url: data.props?.url ?? 'https://placehold.co/600x400@2x/F8F8F8/CCC?text=Your%20image',
url: data.props?.url,
},
};
if (props.props?.url && !isUrl(props.props.url)) {
props.props.url = images.find((i) => i.file.name === props.props.url)?.base64 ?? data.props.url;
}

return (
<EditorBlockWrapper>
<Image {...props} />
Expand Down Expand Up @@ -117,6 +134,14 @@ const EDITOR_DICTIONARY = buildBlockConfigurationDictionary({
</EditorBlockWrapper>
),
},
CustomImage: {
schema: CustomImagePropsSchema,
Component: (props) => (
<EditorBlockWrapper>
<Image {...props} />
</EditorBlockWrapper>
),
},
});

export const EditorBlock = buildBlockComponent(EDITOR_DICTIONARY);
Expand Down