From 499c8bf680e0684d73eb366c34085d469eea71b6 Mon Sep 17 00:00:00 2001 From: Mostafa F Date: Mon, 7 Oct 2024 13:20:31 +0330 Subject: [PATCH 1/5] onlook-dev/onlook/issues/471 --- app/src/routes/editor/LayersPanel/index.tsx | 34 ++++-- .../routes/editor/ThemingPanel/AssetsTab.tsx | 12 +++ .../editor/ThemingPanel/VariablesTab.tsx | 11 ++ app/src/routes/editor/ThemingPanel/index.tsx | 100 ++++++++++++++++++ app/src/routes/editor/index.tsx | 9 +- 5 files changed, 155 insertions(+), 11 deletions(-) create mode 100644 app/src/routes/editor/ThemingPanel/AssetsTab.tsx create mode 100644 app/src/routes/editor/ThemingPanel/VariablesTab.tsx create mode 100644 app/src/routes/editor/ThemingPanel/index.tsx diff --git a/app/src/routes/editor/LayersPanel/index.tsx b/app/src/routes/editor/LayersPanel/index.tsx index e92a0b08..5b2f47a0 100644 --- a/app/src/routes/editor/LayersPanel/index.tsx +++ b/app/src/routes/editor/LayersPanel/index.tsx @@ -2,17 +2,22 @@ import { useEditorEngine } from '@/components/Context'; import { Separator } from '@/components/ui/separator'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { EditorMode } from '@/lib/models'; -import { PinLeftIcon, PinRightIcon } from '@radix-ui/react-icons'; +import { LayersIcon, PinLeftIcon } from '@radix-ui/react-icons'; import clsx from 'clsx'; import { observer } from 'mobx-react-lite'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import ComponentsTab from './ComponentsTab'; import LayersTab from './LayersTab'; import { capitalizeFirstLetter } from '/common/helpers'; const COMPONENT_DISCOVERY_ENABLED = false; -const LayersPanel = observer(() => { +interface LayersPanelProps { + openPanel: 'layers' | 'theming' | null; + setOpenPanel: React.Dispatch>; +} + +const LayersPanel = observer(({ openPanel, setOpenPanel }: LayersPanelProps) => { const editorEngine = useEditorEngine(); enum TabValue { LAYERS = 'layers', @@ -21,10 +26,19 @@ const LayersPanel = observer(() => { const selectedTab: string = TabValue.LAYERS; const [isOpen, setIsOpen] = useState(true); + useEffect(() => { + if (openPanel === 'layers') { + setIsOpen(true); + } else { + setIsOpen(false); + } + }, [openPanel]); + function renderTabs() { return ( + {
-
+
@@ -64,22 +78,22 @@ const LayersPanel = observer(() => { return (
{!isOpen && (
setIsOpen(true)} + onClick={() => setOpenPanel('layers')} > - +
)}
diff --git a/app/src/routes/editor/ThemingPanel/AssetsTab.tsx b/app/src/routes/editor/ThemingPanel/AssetsTab.tsx new file mode 100644 index 00000000..c9507b6b --- /dev/null +++ b/app/src/routes/editor/ThemingPanel/AssetsTab.tsx @@ -0,0 +1,12 @@ +import { observer } from 'mobx-react-lite'; + + +const AssetsTab = observer(() => { + return ( +
+
ASSETS
+
+ ); +}); + +export default AssetsTab; diff --git a/app/src/routes/editor/ThemingPanel/VariablesTab.tsx b/app/src/routes/editor/ThemingPanel/VariablesTab.tsx new file mode 100644 index 00000000..8206bb34 --- /dev/null +++ b/app/src/routes/editor/ThemingPanel/VariablesTab.tsx @@ -0,0 +1,11 @@ +import { observer } from 'mobx-react-lite'; + +const VariablesTab = observer(() => { + return ( +
+
VARIABLES
+
+ ); +}); + +export default VariablesTab; diff --git a/app/src/routes/editor/ThemingPanel/index.tsx b/app/src/routes/editor/ThemingPanel/index.tsx new file mode 100644 index 00000000..ba11b8b8 --- /dev/null +++ b/app/src/routes/editor/ThemingPanel/index.tsx @@ -0,0 +1,100 @@ +import { useEditorEngine } from '@/components/Context'; +import { Separator } from '@/components/ui/separator'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import { EditorMode } from '@/lib/models'; +import { BlendingModeIcon, PinLeftIcon } from '@radix-ui/react-icons'; +import clsx from 'clsx'; +import { observer } from 'mobx-react-lite'; +import { useEffect, useState } from 'react'; +import { capitalizeFirstLetter } from '/common/helpers'; +import AssetsTab from './AssetsTab'; +import VariablesTab from './VariablesTab'; + +interface ThemingPanelProps { + openPanel: 'layers' | 'theming' | null; + setOpenPanel: React.Dispatch>; +} + +const ThemingPanel = observer(({ openPanel, setOpenPanel }: ThemingPanelProps) => { + const editorEngine = useEditorEngine(); + enum TabValue { + ASSETS = 'assets', + VARIABLES = 'variables', + } + const selectedTab: string = TabValue.ASSETS; + const [isOpen, setIsOpen] = useState(false); + + useEffect(() => { + if (openPanel === 'theming') { + setIsOpen(true); + } else { + setIsOpen(false); + } + }, [openPanel]); + + function renderTabs() { + return ( + + + + + {capitalizeFirstLetter(TabValue.ASSETS)} + + + {capitalizeFirstLetter(TabValue.VARIABLES)} + +
+ +
+ +
+ + + + + + +
+
+ ); + } + return ( +
+ {!isOpen && ( +
setOpenPanel('theming')} + > + +
+ )} +
+ {renderTabs()} +
+
+ ); +}); + +export default ThemingPanel; diff --git a/app/src/routes/editor/index.tsx b/app/src/routes/editor/index.tsx index 68262c13..d3e21d32 100644 --- a/app/src/routes/editor/index.tsx +++ b/app/src/routes/editor/index.tsx @@ -1,3 +1,4 @@ +import { useState } from 'react'; import Canvas from './Canvas'; import EditPanel from './EditPanel'; import LayersPanel from './LayersPanel'; @@ -5,8 +6,10 @@ import ResizablePanel from './LayersPanel/ResizablePanel'; import Toolbar from './Toolbar'; import EditorTopBar from './TopBar'; import WebviewArea from './WebviewArea'; +import ThemingPanel from './ThemingPanel'; function ProjectEditor() { + const [openPanel, setOpenPanel] = useState<'layers' | 'theming' | null>('layers'); return ( <>
@@ -14,8 +17,12 @@ function ProjectEditor() { + {/* TODO: ThemingPanel should have same width as LayersPanel, but LayersPanel is resizable */}
- + +
+
+
From 29feeefde63a919156ee858b85d807658f22d331 Mon Sep 17 00:00:00 2001 From: Mostafa F Date: Mon, 7 Oct 2024 13:27:15 +0330 Subject: [PATCH 2/5] simple useEffect --- app/src/routes/editor/LayersPanel/index.tsx | 8 +------- app/src/routes/editor/ThemingPanel/index.tsx | 8 +------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/app/src/routes/editor/LayersPanel/index.tsx b/app/src/routes/editor/LayersPanel/index.tsx index 5b2f47a0..df958c8e 100644 --- a/app/src/routes/editor/LayersPanel/index.tsx +++ b/app/src/routes/editor/LayersPanel/index.tsx @@ -26,13 +26,7 @@ const LayersPanel = observer(({ openPanel, setOpenPanel }: LayersPanelProps) => const selectedTab: string = TabValue.LAYERS; const [isOpen, setIsOpen] = useState(true); - useEffect(() => { - if (openPanel === 'layers') { - setIsOpen(true); - } else { - setIsOpen(false); - } - }, [openPanel]); + useEffect(() => setIsOpen(openPanel === 'layers'), [openPanel]); function renderTabs() { return ( diff --git a/app/src/routes/editor/ThemingPanel/index.tsx b/app/src/routes/editor/ThemingPanel/index.tsx index ba11b8b8..09394c26 100644 --- a/app/src/routes/editor/ThemingPanel/index.tsx +++ b/app/src/routes/editor/ThemingPanel/index.tsx @@ -24,13 +24,7 @@ const ThemingPanel = observer(({ openPanel, setOpenPanel }: ThemingPanelProps) = const selectedTab: string = TabValue.ASSETS; const [isOpen, setIsOpen] = useState(false); - useEffect(() => { - if (openPanel === 'theming') { - setIsOpen(true); - } else { - setIsOpen(false); - } - }, [openPanel]); + useEffect(() => setIsOpen(openPanel === 'theming'), [openPanel]); function renderTabs() { return ( From 65221e4e11e4880229105b73c24b856975fc1e36 Mon Sep 17 00:00:00 2001 From: Mostafa F Date: Mon, 7 Oct 2024 17:42:36 +0330 Subject: [PATCH 3/5] cleanup --- app/src/index.css | 4 ++-- app/src/routes/editor/ThemingPanel/AssetsTab.tsx | 1 - app/src/routes/editor/index.tsx | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/src/index.css b/app/src/index.css index ceec5bf3..34799e67 100644 --- a/app/src/index.css +++ b/app/src/index.css @@ -91,6 +91,6 @@ body, } *:focus-visible { - outline: none !important; - box-shadow: unset !important; + outline: none !important; + box-shadow: unset !important; } diff --git a/app/src/routes/editor/ThemingPanel/AssetsTab.tsx b/app/src/routes/editor/ThemingPanel/AssetsTab.tsx index c9507b6b..cc37396a 100644 --- a/app/src/routes/editor/ThemingPanel/AssetsTab.tsx +++ b/app/src/routes/editor/ThemingPanel/AssetsTab.tsx @@ -1,6 +1,5 @@ import { observer } from 'mobx-react-lite'; - const AssetsTab = observer(() => { return (
diff --git a/app/src/routes/editor/index.tsx b/app/src/routes/editor/index.tsx index d3e21d32..30db211e 100644 --- a/app/src/routes/editor/index.tsx +++ b/app/src/routes/editor/index.tsx @@ -19,10 +19,10 @@ function ProjectEditor() { {/* TODO: ThemingPanel should have same width as LayersPanel, but LayersPanel is resizable */}
- +
- +
From 9bff2a60b7afbf6694bfe530b66b3a09871d9fe3 Mon Sep 17 00:00:00 2001 From: Kiet Ho Date: Mon, 7 Oct 2024 11:08:39 -0400 Subject: [PATCH 4/5] Support open both --- app/src/routes/editor/LayersPanel/index.tsx | 30 ++++++++++++----- .../routes/editor/ThemingPanel/AssetsTab.tsx | 2 +- app/src/routes/editor/ThemingPanel/index.tsx | 32 +++++++++++++------ app/src/routes/editor/index.tsx | 10 +++--- 4 files changed, 51 insertions(+), 23 deletions(-) diff --git a/app/src/routes/editor/LayersPanel/index.tsx b/app/src/routes/editor/LayersPanel/index.tsx index df958c8e..36f0a2dc 100644 --- a/app/src/routes/editor/LayersPanel/index.tsx +++ b/app/src/routes/editor/LayersPanel/index.tsx @@ -13,11 +13,11 @@ import { capitalizeFirstLetter } from '/common/helpers'; const COMPONENT_DISCOVERY_ENABLED = false; interface LayersPanelProps { - openPanel: 'layers' | 'theming' | null; - setOpenPanel: React.Dispatch>; + openPanels: ('layers' | 'theming')[]; + setOpenPanels: React.Dispatch>; } -const LayersPanel = observer(({ openPanel, setOpenPanel }: LayersPanelProps) => { +const LayersPanel = observer(({ openPanels, setOpenPanels }: LayersPanelProps) => { const editorEngine = useEditorEngine(); enum TabValue { LAYERS = 'layers', @@ -26,7 +26,15 @@ const LayersPanel = observer(({ openPanel, setOpenPanel }: LayersPanelProps) => const selectedTab: string = TabValue.LAYERS; const [isOpen, setIsOpen] = useState(true); - useEffect(() => setIsOpen(openPanel === 'layers'), [openPanel]); + useEffect(() => setIsOpen(openPanels.includes('layers')), [openPanels]); + + const togglePanelOpen = () => { + if (isOpen) { + setOpenPanels(openPanels.filter((panel) => panel !== 'layers')); + } else { + setOpenPanels([...openPanels, 'layers']); + } + }; function renderTabs() { return ( @@ -48,13 +56,18 @@ const LayersPanel = observer(({ openPanel, setOpenPanel }: LayersPanelProps) =>
-
+
1 ? 'h-[calc(46vh-5rem)]' : '', + )} + > @@ -75,19 +88,20 @@ const LayersPanel = observer(({ openPanel, setOpenPanel }: LayersPanelProps) => 'left-0 top-20 transition-width duration-300 opacity-100 bg-black/80 rounded-r-xl', editorEngine.mode === EditorMode.INTERACT ? 'hidden' : 'visible', isOpen ? 'w-full h-[calc(90vh-5rem)]' : 'w-12 h-12 rounded-r-xl cursor-pointer', + openPanels.length > 1 ? 'h-[calc(50vh-5rem)]' : '', )} > {!isOpen && (
setOpenPanel('layers')} + onClick={togglePanelOpen} >
)}
diff --git a/app/src/routes/editor/ThemingPanel/AssetsTab.tsx b/app/src/routes/editor/ThemingPanel/AssetsTab.tsx index cc37396a..12be0e20 100644 --- a/app/src/routes/editor/ThemingPanel/AssetsTab.tsx +++ b/app/src/routes/editor/ThemingPanel/AssetsTab.tsx @@ -3,7 +3,7 @@ import { observer } from 'mobx-react-lite'; const AssetsTab = observer(() => { return (
-
ASSETS
+
ASSETS
); }); diff --git a/app/src/routes/editor/ThemingPanel/index.tsx b/app/src/routes/editor/ThemingPanel/index.tsx index 09394c26..3192c575 100644 --- a/app/src/routes/editor/ThemingPanel/index.tsx +++ b/app/src/routes/editor/ThemingPanel/index.tsx @@ -6,16 +6,16 @@ import { BlendingModeIcon, PinLeftIcon } from '@radix-ui/react-icons'; import clsx from 'clsx'; import { observer } from 'mobx-react-lite'; import { useEffect, useState } from 'react'; -import { capitalizeFirstLetter } from '/common/helpers'; import AssetsTab from './AssetsTab'; import VariablesTab from './VariablesTab'; +import { capitalizeFirstLetter } from '/common/helpers'; interface ThemingPanelProps { - openPanel: 'layers' | 'theming' | null; - setOpenPanel: React.Dispatch>; + openPanels: ('layers' | 'theming')[]; + setOpenPanels: React.Dispatch>; } -const ThemingPanel = observer(({ openPanel, setOpenPanel }: ThemingPanelProps) => { +const ThemingPanel = observer(({ openPanels, setOpenPanels }: ThemingPanelProps) => { const editorEngine = useEditorEngine(); enum TabValue { ASSETS = 'assets', @@ -24,7 +24,15 @@ const ThemingPanel = observer(({ openPanel, setOpenPanel }: ThemingPanelProps) = const selectedTab: string = TabValue.ASSETS; const [isOpen, setIsOpen] = useState(false); - useEffect(() => setIsOpen(openPanel === 'theming'), [openPanel]); + useEffect(() => setIsOpen(openPanels.includes('theming')), [openPanels]); + + const togglePanelOpen = () => { + if (isOpen) { + setOpenPanels(openPanels.filter((panel) => panel !== 'theming')); + } else { + setOpenPanels([...openPanels, 'theming']); + } + }; function renderTabs() { return ( @@ -46,13 +54,18 @@ const ThemingPanel = observer(({ openPanel, setOpenPanel }: ThemingPanelProps) =
-
+
1 ? 'h-[calc(51vh-5rem)]' : '', + )} + > @@ -69,19 +82,20 @@ const ThemingPanel = observer(({ openPanel, setOpenPanel }: ThemingPanelProps) = 'left-0 top-20 transition-width duration-300 opacity-100 bg-black/80 rounded-r-xl', editorEngine.mode === EditorMode.INTERACT ? 'hidden' : 'visible', isOpen ? 'w-full h-[calc(90vh-5rem)]' : 'w-12 h-12 rounded-r-xl cursor-pointer', + openPanels.length > 1 ? 'h-[calc(55vh-5rem)] mt-2' : '', )} > {!isOpen && (
setOpenPanel('theming')} + onClick={togglePanelOpen} >
)}
diff --git a/app/src/routes/editor/index.tsx b/app/src/routes/editor/index.tsx index 30db211e..d30570df 100644 --- a/app/src/routes/editor/index.tsx +++ b/app/src/routes/editor/index.tsx @@ -3,13 +3,14 @@ import Canvas from './Canvas'; import EditPanel from './EditPanel'; import LayersPanel from './LayersPanel'; import ResizablePanel from './LayersPanel/ResizablePanel'; +import ThemingPanel from './ThemingPanel'; import Toolbar from './Toolbar'; import EditorTopBar from './TopBar'; import WebviewArea from './WebviewArea'; -import ThemingPanel from './ThemingPanel'; function ProjectEditor() { - const [openPanel, setOpenPanel] = useState<'layers' | 'theming' | null>('layers'); + const [openPanels, setOpenPanels] = useState<('layers' | 'theming')[]>(['layers']); + return ( <>
@@ -17,12 +18,11 @@ function ProjectEditor() { - {/* TODO: ThemingPanel should have same width as LayersPanel, but LayersPanel is resizable */}
- +
- +
From 12af363b4e7f967bc85834529d2433eb8e1e3389 Mon Sep 17 00:00:00 2001 From: Mostafa F Date: Mon, 7 Oct 2024 21:25:21 +0330 Subject: [PATCH 5/5] pixel-perfect multi-panel --- app/src/routes/editor/LayersPanel/LayersTab.tsx | 2 +- app/src/routes/editor/LayersPanel/index.tsx | 12 ++++++++---- app/src/routes/editor/ThemingPanel/VariablesTab.tsx | 2 +- app/src/routes/editor/ThemingPanel/index.tsx | 12 ++++++++---- app/src/routes/editor/index.tsx | 2 +- 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/app/src/routes/editor/LayersPanel/LayersTab.tsx b/app/src/routes/editor/LayersPanel/LayersTab.tsx index 7d3bf81e..eb0d97f9 100644 --- a/app/src/routes/editor/LayersPanel/LayersTab.tsx +++ b/app/src/routes/editor/LayersPanel/LayersTab.tsx @@ -81,7 +81,7 @@ const LayersTab = observer(() => { return (
setTreeHovered(true)} onMouseLeave={() => handleMouseLeaveTree()} > diff --git a/app/src/routes/editor/LayersPanel/index.tsx b/app/src/routes/editor/LayersPanel/index.tsx index 36f0a2dc..bf51e1bb 100644 --- a/app/src/routes/editor/LayersPanel/index.tsx +++ b/app/src/routes/editor/LayersPanel/index.tsx @@ -64,8 +64,8 @@ const LayersPanel = observer(({ openPanels, setOpenPanels }: LayersPanelProps) =
1 ? 'h-[calc(46vh-5rem)]' : '', + 'overflow-auto mx-2', + openPanels.length > 1 ? 'h-[calc(48vh-4.5rem)]' : 'h-[calc(93vh-7.5rem)]', )} > @@ -87,8 +87,12 @@ const LayersPanel = observer(({ openPanels, setOpenPanels }: LayersPanelProps) = className={clsx( 'left-0 top-20 transition-width duration-300 opacity-100 bg-black/80 rounded-r-xl', editorEngine.mode === EditorMode.INTERACT ? 'hidden' : 'visible', - isOpen ? 'w-full h-[calc(90vh-5rem)]' : 'w-12 h-12 rounded-r-xl cursor-pointer', - openPanels.length > 1 ? 'h-[calc(50vh-5rem)]' : '', + isOpen ? 'w-full' : 'w-12 h-12 rounded-r-xl cursor-pointer', + isOpen + ? openPanels.length > 1 + ? 'h-[calc(52.5vh-5rem)]' + : 'h-[calc(93vh-5rem)]' + : '', )} > {!isOpen && ( diff --git a/app/src/routes/editor/ThemingPanel/VariablesTab.tsx b/app/src/routes/editor/ThemingPanel/VariablesTab.tsx index 8206bb34..317b4eb1 100644 --- a/app/src/routes/editor/ThemingPanel/VariablesTab.tsx +++ b/app/src/routes/editor/ThemingPanel/VariablesTab.tsx @@ -3,7 +3,7 @@ import { observer } from 'mobx-react-lite'; const VariablesTab = observer(() => { return (
-
VARIABLES
+
VARIABLES
); }); diff --git a/app/src/routes/editor/ThemingPanel/index.tsx b/app/src/routes/editor/ThemingPanel/index.tsx index 3192c575..222bd237 100644 --- a/app/src/routes/editor/ThemingPanel/index.tsx +++ b/app/src/routes/editor/ThemingPanel/index.tsx @@ -62,8 +62,8 @@ const ThemingPanel = observer(({ openPanels, setOpenPanels }: ThemingPanelProps)
1 ? 'h-[calc(51vh-5rem)]' : '', + 'overflow-auto mx-2', + openPanels.length > 1 ? 'h-[calc(48vh-4.5rem)]' : 'h-[calc(93vh-7.5rem)]', )} > @@ -81,8 +81,12 @@ const ThemingPanel = observer(({ openPanels, setOpenPanels }: ThemingPanelProps) className={clsx( 'left-0 top-20 transition-width duration-300 opacity-100 bg-black/80 rounded-r-xl', editorEngine.mode === EditorMode.INTERACT ? 'hidden' : 'visible', - isOpen ? 'w-full h-[calc(90vh-5rem)]' : 'w-12 h-12 rounded-r-xl cursor-pointer', - openPanels.length > 1 ? 'h-[calc(55vh-5rem)] mt-2' : '', + isOpen ? 'w-full' : 'w-12 h-12 rounded-r-xl cursor-pointer', + isOpen + ? openPanels.length > 1 + ? 'h-[calc(52.5vh-5rem)]' + : 'h-[calc(93vh-5rem)]' + : '', )} > {!isOpen && ( diff --git a/app/src/routes/editor/index.tsx b/app/src/routes/editor/index.tsx index d30570df..19edca8b 100644 --- a/app/src/routes/editor/index.tsx +++ b/app/src/routes/editor/index.tsx @@ -21,7 +21,7 @@ function ProjectEditor() {
-
+