From 62f0dd7c67de7a4d70d216c52c9807f3f8ce5e5c Mon Sep 17 00:00:00 2001 From: Leonardo Christino <leomilho@gmail.com> Date: Fri, 29 Mar 2024 15:09:46 +0100 Subject: [PATCH] chore: fix resizable when empty children --- apps/web/src/app/app.tsx | 49 +++++++- .../src/components/onboarding/onboarding.tsx | 2 +- libs/shared/lib/components/Resizable.tsx | 34 ++++- libs/shared/lib/sidebar/index.tsx | 116 ++++++------------ 4 files changed, 113 insertions(+), 88 deletions(-) diff --git a/apps/web/src/app/app.tsx b/apps/web/src/app/app.tsx index 259f2b7f0..867825d78 100644 --- a/apps/web/src/app/app.tsx +++ b/apps/web/src/app/app.tsx @@ -13,14 +13,19 @@ import { Navbar } from '../components/navbar/navbar'; import { Resizable } from '@graphpolaris/shared/lib/components/Resizable'; import { DashboardAlerts } from '@graphpolaris/shared/lib/data-access/authorization/dashboardAlerts'; import { EventBus } from '@graphpolaris/shared/lib/data-access/api/eventBus'; -import Onboarding from '../components/onboarding/onboarding'; +import { Onboarding } from '../components/onboarding/onboarding'; import { wsQueryRequest } from '@graphpolaris/shared/lib/data-access/broker'; import { URLParams, setParam } from '@graphpolaris/shared/lib/data-access/api/url'; import { VisualizationPanel } from '@graphpolaris/shared/lib/vis'; import { QueryBuilder } from '@graphpolaris/shared/lib/querybuilder'; -import Sidebar from '@graphpolaris/shared/lib/sidebar'; +import { SideNavTab, Sidebar } from '@graphpolaris/shared/lib/sidebar'; import { useVisualizationManager } from '@graphpolaris/shared/lib/vis/hooks'; import { ConfigPanel } from '@graphpolaris/shared/lib/vis/components/config'; +import { Tooltip, TooltipTrigger, Button, TooltipContent, TooltipProvider } from '@graphpolaris/shared'; +import ControlContainer from '@graphpolaris/shared/lib/components/controls'; +import Searchbar from '@graphpolaris/shared/lib/sidebar/search/searchbar'; +import Schema from '@graphpolaris/shared/lib/schema/panel'; +import { Fullscreen, Schema as SchemaIcon, Search as SearchIcon } from '@mui/icons-material'; export type App = { load?: string; @@ -53,6 +58,7 @@ export function App(props: App) { }, [props]); const [authCheck, setAuthCheck] = useState(false); + const [tab, setTab] = useState<SideNavTab>('Schema'); return ( <div className="h-screen w-screen overflow-clip"> @@ -71,10 +77,43 @@ export function App(props: App) { <aside className="h-auto w-auto"> <Navbar /> </aside> - <main className="flex w-screen h-[calc(100%-4.2rem)]"> + <main className="flex w-screen h-[calc(100%-4.2rem)] flex-row"> + <Sidebar onTab={(tab) => setTab(tab)} /> <Resizable divisorSize={3} horizontal={true} defaultProportion={0.33}> - <Sidebar auth={authCheck} /> - <div className="h-full w-full"> + {tab !== undefined ? ( + <div className="flex-1 border overflow-hidden w-full h-full"> + <div className="relative flex items-center justify-between z-[2] py-0 px-2 bg-secondary-100 border-b"> + <h1 className="text-xs font-semibold text-secondary-800">{tab}</h1> + <ControlContainer> + <TooltipProvider delayDuration={100}> + {tab === 'Schema' && ( + <Tooltip> + <TooltipTrigger asChild> + <Button type="secondary" variant="ghost" size="xs" iconComponent={<Fullscreen />} onClick={() => {}} /> + </TooltipTrigger> + <TooltipContent side={'bottom'}> + <p>Fit to screen</p> + </TooltipContent> + </Tooltip> + )} + {tab === 'Search' && ( + <Tooltip> + <TooltipTrigger asChild> + <Button type="secondary" variant="ghost" size="xs" iconComponent={<Fullscreen />} onClick={() => {}} /> + </TooltipTrigger> + <TooltipContent side={'bottom'}> + <p>Mock icon</p> + </TooltipContent> + </Tooltip> + )} + </TooltipProvider> + </ControlContainer> + </div> + {tab === 'Search' && <Searchbar />} + {tab === 'Schema' && <Schema auth={authCheck} />} + </div> + ) : null} + <div className="h-full w-full flex-grow"> <Resizable divisorSize={3} horizontal={false}> <div className="w-full h-full border"> <VisualizationPanel manager={manager} /> diff --git a/apps/web/src/components/onboarding/onboarding.tsx b/apps/web/src/components/onboarding/onboarding.tsx index e7526d3f8..d811f1df2 100644 --- a/apps/web/src/components/onboarding/onboarding.tsx +++ b/apps/web/src/components/onboarding/onboarding.tsx @@ -11,7 +11,7 @@ interface OnboardingState { stepIndex?: number; } -export default function Onboarding({}) { +export function Onboarding({}) { const location = useLocation(); const auth = useAuthorizationCache(); const [showWalkthrough, setShowWalkthrough] = useState<boolean>(false); diff --git a/libs/shared/lib/components/Resizable.tsx b/libs/shared/lib/components/Resizable.tsx index 522f011b6..2fd89d32a 100644 --- a/libs/shared/lib/components/Resizable.tsx +++ b/libs/shared/lib/components/Resizable.tsx @@ -7,13 +7,25 @@ type Props = { horizontal: boolean; divisorSize: number; defaultProportion?: number; + classNameLeft?: string; + classNameRight?: string; }; function convertRemToPixels(rem: number) { return rem * parseFloat(getComputedStyle(document.documentElement).fontSize); } -export const Resizable = ({ children, className, style, horizontal, divisorSize, defaultProportion, ...props }: Props) => { +export const Resizable = ({ + children, + className, + style, + horizontal, + divisorSize, + defaultProportion, + classNameLeft, + classNameRight, + ...props +}: Props) => { const ref = useRef<HTMLDivElement>(null); const children2 = children as React.ReactElement[]; const [firstSize, setFirstSize] = React.useState<number>(0); @@ -65,13 +77,24 @@ export const Resizable = ({ children, className, style, horizontal, divisorSize, setDragging(false); } + if (!children2[0]) return children2[1]; + if (!children2[1]) return children2[0]; + return ( <> {dragging && <div className="absolute top-0 left-0 w-screen h-screen z-10 cursor-grabbing" onMouseMove={onMouseMove}></div>} - <div className={` w-full h-full flex ${horizontal ? 'flex-row' : 'flex-col'} ${className}`} style={style} {...props} ref={ref}> + <div + className={`w-full h-full flex flex-grow ${horizontal ? 'flex-row' : 'flex-col'} ${className !== undefined ? className : ''} `} + style={style} + {...props} + ref={ref} + > {firstSize > 0 && ( <> - <div className="h-full w-full" style={horizontal ? { maxWidth: firstSize } : { maxHeight: firstSize }}> + <div + className={'h-full w-full' + (classNameLeft !== undefined ? classNameLeft : '')} + style={horizontal ? { maxWidth: firstSize } : { maxHeight: firstSize }} + > {children2[0]} </div> <div @@ -83,7 +106,10 @@ export const Resizable = ({ children, className, style, horizontal, divisorSize, onTouchEnd={onTouchEnd} onTouchCancel={onTouchCancel} ></div> - <div className="h-full w-full" style={horizontal ? { maxWidth: secondSize } : { maxHeight: secondSize }}> + <div + className={'h-full w-full ' + (classNameRight !== undefined ? classNameRight : '')} + style={horizontal ? { maxWidth: secondSize } : { maxHeight: secondSize }} + > {children2[1]} </div> </> diff --git a/libs/shared/lib/sidebar/index.tsx b/libs/shared/lib/sidebar/index.tsx index 5af63b646..f84d03631 100644 --- a/libs/shared/lib/sidebar/index.tsx +++ b/libs/shared/lib/sidebar/index.tsx @@ -1,94 +1,54 @@ import React, { useState } from 'react'; import { Button, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../components'; import { Fullscreen, Schema as SchemaIcon, Search as SearchIcon } from '@mui/icons-material'; -import Schema from '../schema/panel'; -import Searchbar from './search/searchbar'; import ColorMode from '../components/color-mode'; -import ControlContainer from '../components/controls'; -export default function Sidebar({ auth }: { auth: boolean }) { - const [tab, setTab] = useState('Schema'); - const [showDialog, setShowDialog] = useState<boolean>(true); +export type SideNavTab = 'Schema' | 'Search' | undefined; +const tabs: Array<{ + name: SideNavTab; + icon: JSX.Element; +}> = [ + { + name: 'Search', + icon: <SearchIcon />, + }, + { name: 'Schema', icon: <SchemaIcon /> }, +]; + +export function Sidebar({ onTab }: { onTab: (tab: SideNavTab) => void }) { + const [tab, setTab] = useState<SideNavTab>('Schema'); return ( - <div className="info-panel w-full h-full flex"> + <div className="info-panel w-fit h-full flex flex-shrink"> <TooltipProvider delayDuration={100}> <div className="w-12 flex flex-col items-center justify-between"> <div className="flex flex-col items-center justify-between"> - <Tooltip> - <TooltipTrigger> - <Button - type="secondary" - variant="ghost" - size="md" - iconComponent={<SearchIcon />} - onClick={() => { - if (!showDialog) { - setShowDialog(true); - } else if (tab === 'Search') { - setShowDialog(false); - } - setTab('Search'); - }} - additionalClasses={tab === 'Search' ? 'bg-secondary-100' : ''} - /> - </TooltipTrigger> - <TooltipContent side={'right'}>Search</TooltipContent> - </Tooltip> - <Tooltip> - <TooltipTrigger> - <Button - type="secondary" - variant="ghost" - size="md" - iconComponent={<SchemaIcon />} - onClick={() => { - if (!showDialog) { - setShowDialog(true); - } else if (tab === 'Schema') { - setShowDialog(false); - } - setTab('Schema'); - }} - additionalClasses={tab === 'Schema' ? 'bg-secondary-100' : ''} - /> - </TooltipTrigger> - <TooltipContent side={'right'}>Database schema</TooltipContent> - </Tooltip> + {tabs.map((t) => ( + <Tooltip key={t.name}> + <TooltipTrigger> + <Button + type="secondary" + variant="ghost" + size="md" + iconComponent={t.icon} + onClick={() => { + if (tab === t.name) { + onTab(undefined); + setTab(undefined); + } else { + onTab(t.name); + setTab(t.name); + } + }} + additionalClasses={tab === t.name ? 'bg-secondary-100' : ''} + /> + </TooltipTrigger> + <TooltipContent side={'right'}>{t.name}</TooltipContent> + </Tooltip> + ))} </div> <ColorMode /> </div> - {showDialog && ( - <div className="flex-1 border overflow-hidden"> - <div className="relative flex items-center justify-between z-[2] py-0 px-2 bg-secondary-100 border-b"> - <h1 className="text-xs font-semibold text-secondary-800">{tab}</h1> - <ControlContainer> - {tab === 'Schema' && ( - <Tooltip> - <TooltipTrigger asChild> - <Button type="secondary" variant="ghost" size="xs" iconComponent={<Fullscreen />} onClick={() => {}} /> - </TooltipTrigger> - <TooltipContent side={'bottom'}> - <p>Fit to screen</p> - </TooltipContent> - </Tooltip> - )} - {tab === 'Search' && ( - <Tooltip> - <TooltipTrigger asChild> - <Button type="secondary" variant="ghost" size="xs" iconComponent={<Fullscreen />} onClick={() => {}} /> - </TooltipTrigger> - <TooltipContent side={'bottom'}> - <p>Mock icon</p> - </TooltipContent> - </Tooltip> - )} - </ControlContainer> - </div> - {tab === 'Search' && <Searchbar />} - {tab === 'Schema' && <Schema auth={auth} />} - </div> - )} </TooltipProvider> </div> ); -- GitLab