From 96352c747a4d9d8d68358904205299771a722959 Mon Sep 17 00:00:00 2001 From: Lucio Sandrini Date: Fri, 13 Sep 2024 15:22:53 -0300 Subject: [PATCH] feat: add upload image capability --- .../input-panels/ImageSidebarPanel.tsx | 35 ++++++++++++++++++- .../src/documents/editor/EditorContext.tsx | 17 +++++++++ .../src/documents/editor/core.tsx | 27 +++++++++++++- 3 files changed, 77 insertions(+), 2 deletions(-) diff --git a/packages/editor-sample/src/App/InspectorDrawer/ConfigurationPanel/input-panels/ImageSidebarPanel.tsx b/packages/editor-sample/src/App/InspectorDrawer/ConfigurationPanel/input-panels/ImageSidebarPanel.tsx index 97f0822..c63e495 100644 --- a/packages/editor-sample/src/App/InspectorDrawer/ConfigurationPanel/input-panels/ImageSidebarPanel.tsx +++ b/packages/editor-sample/src/App/InspectorDrawer/ConfigurationPanel/input-panels/ImageSidebarPanel.tsx @@ -5,7 +5,7 @@ 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'; @@ -13,6 +13,7 @@ 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; @@ -20,6 +21,7 @@ type ImageSidebarPanelProps = { }; export default function ImageSidebarPanel({ data, setData }: ImageSidebarPanelProps) { const [, setErrors] = useState(null); + const images = useImages(); const updateData = (d: unknown) => { const res = ImagePropsSchema.safeParse(d); @@ -31,8 +33,39 @@ export default function ImageSidebarPanel({ data, setData }: ImageSidebarPanelPr } }; + const handleFileUpload = (event: React.ChangeEvent) => { + 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 ( + + + + {images.map((image) => ( + + Uploaded + {image.file.name} + + ))} + + ; }; const editorStateStore = create(() => ({ @@ -25,6 +32,7 @@ const editorStateStore = create(() => ({ inspectorDrawerOpen: true, samplesDrawerOpen: true, + images: [], })); export function useDocument() { @@ -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 = {}; @@ -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] }); +}; diff --git a/packages/editor-sample/src/documents/editor/core.tsx b/packages/editor-sample/src/documents/editor/core.tsx index 4db26f1..f678489 100644 --- a/packages/editor-sample/src/documents/editor/core.tsx +++ b/packages/editor-sample/src/documents/editor/core.tsx @@ -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: { @@ -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 ( @@ -117,6 +134,14 @@ const EDITOR_DICTIONARY = buildBlockConfigurationDictionary({ ), }, + CustomImage: { + schema: CustomImagePropsSchema, + Component: (props) => ( + + + + ), + }, }); export const EditorBlock = buildBlockComponent(EDITOR_DICTIONARY);