From 3acacdcea96e6d363beb7dcfd9bb22b9d18b1742 Mon Sep 17 00:00:00 2001 From: Osama Najjar Date: Thu, 6 Apr 2023 04:16:49 +0200 Subject: [PATCH] feat: add preset support --- src/desginer/Components/PresetCard.tsx | 15 ++++ src/desginer/Components/PresetList.tsx | 34 ++++++++ src/desginer/Components/index.tsx | 1 + src/desginer/PreviewViewport.tsx | 15 ++-- .../GlobalSettings/globalSettingsContext.tsx | 50 ----------- .../Providers/GlobalSettings/index.ts | 1 - src/desginer/Providers/webrepub/index.ts | 1 + .../Providers/webrepub/webrepubContext.tsx | 86 +++++++++++++++++++ .../WebPage/WebPageComponent.tsx | 4 +- src/desginer/index.tsx | 13 +-- src/desginer/typings/webrepub.d.ts | 7 +- src/pages/_app.tsx | 22 ++++- src/pages/editor/index.tsx | 34 ++++++++ src/pages/index.tsx | 50 +++++------ src/presets/index.ts | 6 ++ .../Components/Html/HtmlComponent.tsx | 4 +- src/presets/react-email/Components/index.ts | 1 + src/presets/react-email/index.ts | 9 ++ 18 files changed, 244 insertions(+), 109 deletions(-) create mode 100644 src/desginer/Components/PresetCard.tsx create mode 100644 src/desginer/Components/PresetList.tsx delete mode 100644 src/desginer/Providers/GlobalSettings/globalSettingsContext.tsx delete mode 100644 src/desginer/Providers/GlobalSettings/index.ts create mode 100644 src/desginer/Providers/webrepub/index.ts create mode 100644 src/desginer/Providers/webrepub/webrepubContext.tsx create mode 100644 src/pages/editor/index.tsx create mode 100644 src/presets/index.ts create mode 100644 src/presets/react-email/Components/index.ts create mode 100644 src/presets/react-email/index.ts diff --git a/src/desginer/Components/PresetCard.tsx b/src/desginer/Components/PresetCard.tsx new file mode 100644 index 0000000..d70f3ad --- /dev/null +++ b/src/desginer/Components/PresetCard.tsx @@ -0,0 +1,15 @@ +import { Panel, Placeholder } from 'rsuite'; + +import { WebrepubPreset } from '@/desginer/typings/webrepub'; + +interface PresetCardProps extends Pick { + onClick: () => void; +} + +export function PresetCard({ name, onClick }: PresetCardProps) { + return ( + + + + ); +} diff --git a/src/desginer/Components/PresetList.tsx b/src/desginer/Components/PresetList.tsx new file mode 100644 index 0000000..22e7c9b --- /dev/null +++ b/src/desginer/Components/PresetList.tsx @@ -0,0 +1,34 @@ +import { useRouter } from 'next/router'; +import { useCallback } from 'react'; +import { Col, Grid, Row } from 'rsuite'; + +import { PresetCard } from '@/desginer/Components/PresetCard'; +import { useWebrepub } from '@/desginer/Providers/webrepub'; + +export function PresetList() { + const { + presets: { collection }, + setCurrentPreset, + } = useWebrepub(); + const router = useRouter(); + + const handlePresetClick = useCallback( + (presetName: string) => { + setCurrentPreset(presetName); + router.push('/editor'); + }, + [router, setCurrentPreset] + ); + + return ( + + + {collection.map(({ name }) => ( + + handlePresetClick(name)} /> + + ))} + + + ); +} diff --git a/src/desginer/Components/index.tsx b/src/desginer/Components/index.tsx index 1de5339..3cf1c67 100644 --- a/src/desginer/Components/index.tsx +++ b/src/desginer/Components/index.tsx @@ -2,3 +2,4 @@ export * from './Drawer'; export * from './ExamplesList'; export * from './Logo'; export * from './MenuButton'; +export * from './PresetCard'; diff --git a/src/desginer/PreviewViewport.tsx b/src/desginer/PreviewViewport.tsx index 79f0f52..2cc8ffa 100644 --- a/src/desginer/PreviewViewport.tsx +++ b/src/desginer/PreviewViewport.tsx @@ -1,12 +1,11 @@ import { useRouter } from 'next/router'; import { useEffect, useState } from 'react'; import ReactDOM from 'react-dom'; -import { FaHome } from 'react-icons/fa'; +import { TiArrowBack } from 'react-icons/ti'; import styled from 'styled-components'; import { EditorContext } from '@/desginer/EditorContext'; import { PreviewFrame } from '@/desginer/frames/PreviewFrame'; -import { GlobalSettingsProvider } from '@/desginer/Providers/GlobalSettings'; const FloatingButton = styled.button` position: fixed; @@ -33,11 +32,11 @@ const FloatingButton = styled.button` function GoHomeButton() { const router = useRouter(); - const goHome = () => router.push('/'); + const goHome = () => router.push('/editor'); return ( - + ); } @@ -49,11 +48,9 @@ export function PreviewViewport() { }, []); return ( <> - - - - - + + + {documentReady ? ReactDOM.createPortal(, document.body) : null} diff --git a/src/desginer/Providers/GlobalSettings/globalSettingsContext.tsx b/src/desginer/Providers/GlobalSettings/globalSettingsContext.tsx deleted file mode 100644 index 0479c7b..0000000 --- a/src/desginer/Providers/GlobalSettings/globalSettingsContext.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { createContext, PropsWithChildren, useContext, useState } from 'react'; - -interface GlobalSettings { - isInDesignMode: boolean; -} - -interface GlobalSettingsContextProps { - settings: GlobalSettings; - setSettings: (settings: Partial) => void; -} - -const GlobalSettingsContext = createContext({ - settings: { - isInDesignMode: true, - }, - setSettings: () => { - /* noop */ - }, -}); - -interface GlobalSettingsProviderProps extends PropsWithChildren { - settings: GlobalSettings; -} - -export function GlobalSettingsProvider({ - children, - settings, -}: GlobalSettingsProviderProps) { - const [globalSettings, setGlobalSettings] = useState(settings); - - const value: GlobalSettingsContextProps = { - settings: globalSettings, - setSettings: (settings) => { - setGlobalSettings({ - ...globalSettings, - ...settings, - }); - }, - }; - - return ( - - {children} - - ); -} - -export function useGlobalSettings() { - return useContext(GlobalSettingsContext); -} diff --git a/src/desginer/Providers/GlobalSettings/index.ts b/src/desginer/Providers/GlobalSettings/index.ts deleted file mode 100644 index 15879c3..0000000 --- a/src/desginer/Providers/GlobalSettings/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './globalSettingsContext'; diff --git a/src/desginer/Providers/webrepub/index.ts b/src/desginer/Providers/webrepub/index.ts new file mode 100644 index 0000000..c0f45c2 --- /dev/null +++ b/src/desginer/Providers/webrepub/index.ts @@ -0,0 +1 @@ +export * from './webrepubContext'; diff --git a/src/desginer/Providers/webrepub/webrepubContext.tsx b/src/desginer/Providers/webrepub/webrepubContext.tsx new file mode 100644 index 0000000..1f4a381 --- /dev/null +++ b/src/desginer/Providers/webrepub/webrepubContext.tsx @@ -0,0 +1,86 @@ +import { createContext, PropsWithChildren, useContext, useState } from 'react'; + +import { WebrepubPreset } from '@/desginer/typings/webrepub'; +import { ensure } from '@/utils'; + +interface WebrepubConfigurations { + settings: { + isInDesignMode: boolean; + }; + presets: { + collection: WebrepubPreset[]; + currentPreset?: WebrepubPreset; + }; +} + +const noop = () => { + /* do nothing */ +}; + +const defaultWebrepubConfigurations: WebrepubConfigurations = { + settings: { + isInDesignMode: true, + }, + presets: { + collection: [], + }, +}; + +interface WebrepubConfigurationsContextProps { + settings: WebrepubConfigurations['settings']; + presets: WebrepubConfigurations['presets']; + setSettings: (settings: Partial) => void; + setCurrentPreset: (presetName: string) => void; +} + +const WebrepubContext = createContext({ + ...defaultWebrepubConfigurations, + setSettings: noop, + setCurrentPreset: noop, +}); + +interface GlobalSettingsProviderProps extends PropsWithChildren { + settings: WebrepubConfigurations['settings']; + presets: WebrepubConfigurations['presets']; +} + +export function WebrepubProvider({ + children, + settings, + presets, +}: GlobalSettingsProviderProps) { + const [globalSettings, setGlobalSettings] = useState(settings); + const [presetsValue, setPresetsValue] = useState(presets); + + const value: WebrepubConfigurationsContextProps = { + settings: globalSettings, + setSettings: (settings) => { + setGlobalSettings({ + ...globalSettings, + ...settings, + }); + }, + presets: { + collection: presetsValue.collection, + currentPreset: presetsValue.currentPreset, + }, + setCurrentPreset: (presetName: string) => { + setPresetsValue({ + ...presetsValue, + currentPreset: ensure( + presetsValue.collection.find((p) => p.name === presetName) + ), + }); + }, + }; + + return ( + + {children} + + ); +} + +export function useWebrepub() { + return useContext(WebrepubContext); +} diff --git a/src/desginer/designComponents/WebPage/WebPageComponent.tsx b/src/desginer/designComponents/WebPage/WebPageComponent.tsx index 831ff4d..f099db1 100644 --- a/src/desginer/designComponents/WebPage/WebPageComponent.tsx +++ b/src/desginer/designComponents/WebPage/WebPageComponent.tsx @@ -3,7 +3,7 @@ import { CSSProperties, useEffect, useState } from 'react'; import styled, { css, CSSObject } from 'styled-components'; import { WebPageComponentSettings } from '@/desginer/designComponents/WebPage/WebPageComponentSettings'; -import { useGlobalSettings } from '@/desginer/Providers/GlobalSettings'; +import { useWebrepub } from '@/desginer/Providers/webrepub'; const defaultConfiguredStyles: CSSObject = { backgroundColor: '#fff', @@ -44,7 +44,7 @@ export const WebPageComponent: UserComponent = ({ const { settings: { isInDesignMode }, - } = useGlobalSettings(); + } = useWebrepub(); useEffect(() => { if (!window) { diff --git a/src/desginer/index.tsx b/src/desginer/index.tsx index 5f2a17c..d298c2b 100644 --- a/src/desginer/index.tsx +++ b/src/desginer/index.tsx @@ -21,7 +21,6 @@ import { ComponentsBar } from '@/desginer/ComponentsBox'; import { DesignViewport } from '@/desginer/DesignViewport'; import { EditorContext } from '@/desginer/EditorContext'; import { PropertiesBox } from '@/desginer/PropertiesBox'; -import { GlobalSettingsProvider } from '@/desginer/Providers/GlobalSettings'; import { ActionsComponent, HistoryComponent, @@ -200,14 +199,8 @@ function Designer() { export function WebRepubApp() { return ( - - - - - + + + ); } diff --git a/src/desginer/typings/webrepub.d.ts b/src/desginer/typings/webrepub.d.ts index aa1d8c5..4fe6e20 100644 --- a/src/desginer/typings/webrepub.d.ts +++ b/src/desginer/typings/webrepub.d.ts @@ -1,6 +1,11 @@ import { UserComponent } from '@craftjs/core'; import { ReactNode } from 'react'; -export type WebrepubComponent = UserComponent & { +export type WebrepubComponent = UserComponent & { icon?: ReactNode; }; + +export type WebrepubPreset = { + name: string; + components: WebrepubComponent[]; +}; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 9710b91..9ebfbfd 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -6,12 +6,26 @@ import '@/styles/globals.css'; import 'rsuite/dist/rsuite.min.css'; import '@/styles/designer.css'; +import { WebrepubProvider } from '@/desginer/Providers/webrepub'; +import { getPresets } from '@/presets'; +import ReactEmailPreset from '@/presets/react-email'; + function MyApp({ Component, pageProps }: AppProps) { return ( - - - - + + + + + + ); } diff --git a/src/pages/editor/index.tsx b/src/pages/editor/index.tsx new file mode 100644 index 0000000..9b9912a --- /dev/null +++ b/src/pages/editor/index.tsx @@ -0,0 +1,34 @@ +import { useEffect, useState } from 'react'; + +import { WebRepubApp } from '@/desginer'; +import { isMobileDevice, isSmallScreen } from '@/desginer/utils/screen'; +import { LoadingView, SmallScreenView } from '@/desginer/Views'; + +export default function EditorPage() { + const [loading, setLoading] = useState(true); + const [isDesktop, setIsDesktop] = useState(false); + + useEffect(() => { + setLoading(false); + + const checkIsDesktop = () => + setIsDesktop(!isMobileDevice() && !isSmallScreen()); + + checkIsDesktop(); + window.addEventListener('resize', checkIsDesktop); + + return () => { + window.removeEventListener('resize', checkIsDesktop); + }; + }, []); + + if (loading) { + return ; + } + + if (isDesktop) { + return ; + } + + return ; +} diff --git a/src/pages/index.tsx b/src/pages/index.tsx index e161b74..b9b0384 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,34 +1,24 @@ -import { useEffect, useState } from 'react'; +import { Divider, Stack } from 'rsuite'; -import { WebRepubApp } from '@/desginer'; -import { isMobileDevice, isSmallScreen } from '@/desginer/utils/screen'; -import { LoadingView, SmallScreenView } from '@/desginer/Views'; +import { Logo } from '@/desginer/Components'; +import { PresetList } from '@/desginer/Components/PresetList'; export default function HomePage() { - const [loading, setLoading] = useState(true); - const [isDesktop, setIsDesktop] = useState(false); - - useEffect(() => { - setLoading(false); - - const checkIsDesktop = () => - setIsDesktop(!isMobileDevice() && !isSmallScreen()); - - checkIsDesktop(); - window.addEventListener('resize', checkIsDesktop); - - return () => { - window.removeEventListener('resize', checkIsDesktop); - }; - }, []); - - if (loading) { - return ; - } - - if (isDesktop) { - return ; - } - - return ; + return ( +
+ + + + + +

Presets

+

select a preset

+
+ + + + +
+
+ ); } diff --git a/src/presets/index.ts b/src/presets/index.ts new file mode 100644 index 0000000..d8c07c1 --- /dev/null +++ b/src/presets/index.ts @@ -0,0 +1,6 @@ +import { WebrepubPreset } from '@/desginer/typings/webrepub'; +import ReactEmailPreset from '@/presets/react-email'; + +export function getPresets(): WebrepubPreset[] { + return [ReactEmailPreset]; +} diff --git a/src/presets/react-email/Components/Html/HtmlComponent.tsx b/src/presets/react-email/Components/Html/HtmlComponent.tsx index c584365..bbe8ee0 100644 --- a/src/presets/react-email/Components/Html/HtmlComponent.tsx +++ b/src/presets/react-email/Components/Html/HtmlComponent.tsx @@ -3,7 +3,7 @@ import { Html, HtmlProps } from '@react-email/html'; import { CSSProperties } from 'react'; import { BsFiletypeHtml } from 'react-icons/bs'; -import { useGlobalSettings } from '@/desginer/Providers/GlobalSettings'; +import { useWebrepub } from '@/desginer/Providers/webrepub'; import { WebrepubComponent } from '@/desginer/typings/webrepub'; import { HtmlComponentSettings } from '@/presets/react-email/Components/Html/HtmlComponentSettings'; @@ -26,7 +26,7 @@ export const HTMLComponent: WebrepubComponent = ({ const { settings: { isInDesignMode: _isInDesignMode }, - } = useGlobalSettings(); + } = useWebrepub(); return (