mirror of
https://github.com/chartdb/chartdb.git
synced 2025-11-12 18:06:35 +00:00
add drawer to side panel on mobile
This commit is contained in:
16
package-lock.json
generated
16
package-lock.json
generated
@@ -47,7 +47,8 @@
|
||||
"react-use": "^17.5.1",
|
||||
"tailwind-merge": "^2.4.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"timeago-react": "^3.0.6"
|
||||
"timeago-react": "^3.0.6",
|
||||
"vaul": "^0.9.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.1.0",
|
||||
@@ -9484,6 +9485,19 @@
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vaul": {
|
||||
"version": "0.9.1",
|
||||
"resolved": "https://registry.npmjs.org/vaul/-/vaul-0.9.1.tgz",
|
||||
"integrity": "sha512-fAhd7i4RNMinx+WEm6pF3nOl78DFkAazcN04ElLPFF9BMCNGbY/kou8UMhIcicm0rJCNePJP0Yyza60gGOD0Jw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-dialog": "^1.0.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8 || ^17.0 || ^18.0",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "5.3.5",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.3.5.tgz",
|
||||
|
||||
@@ -51,7 +51,8 @@
|
||||
"react-use": "^17.5.1",
|
||||
"tailwind-merge": "^2.4.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"timeago-react": "^3.0.6"
|
||||
"timeago-react": "^3.0.6",
|
||||
"vaul": "^0.9.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.1.0",
|
||||
|
||||
116
src/components/drawer/drawer.tsx
Normal file
116
src/components/drawer/drawer.tsx
Normal file
@@ -0,0 +1,116 @@
|
||||
import React from 'react';
|
||||
import { Drawer as DrawerPrimitive } from 'vaul';
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const Drawer = ({
|
||||
shouldScaleBackground = true,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DrawerPrimitive.Root>) => (
|
||||
<DrawerPrimitive.Root
|
||||
shouldScaleBackground={shouldScaleBackground}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
Drawer.displayName = 'Drawer';
|
||||
|
||||
const DrawerTrigger = DrawerPrimitive.Trigger;
|
||||
|
||||
const DrawerPortal = DrawerPrimitive.Portal;
|
||||
|
||||
const DrawerClose = DrawerPrimitive.Close;
|
||||
|
||||
const DrawerOverlay = React.forwardRef<
|
||||
React.ElementRef<typeof DrawerPrimitive.Overlay>,
|
||||
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Overlay>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DrawerPrimitive.Overlay
|
||||
ref={ref}
|
||||
className={cn('fixed inset-0 z-50 bg-black/80', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName;
|
||||
|
||||
const DrawerContent = React.forwardRef<
|
||||
React.ElementRef<typeof DrawerPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<DrawerPortal>
|
||||
<DrawerOverlay />
|
||||
<DrawerPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border bg-background',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div className="mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted" />
|
||||
{children}
|
||||
</DrawerPrimitive.Content>
|
||||
</DrawerPortal>
|
||||
));
|
||||
DrawerContent.displayName = 'DrawerContent';
|
||||
|
||||
const DrawerHeader = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn('grid gap-1.5 p-4 text-center sm:text-left', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
DrawerHeader.displayName = 'DrawerHeader';
|
||||
|
||||
const DrawerFooter = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn('mt-auto flex flex-col gap-2 p-4', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
DrawerFooter.displayName = 'DrawerFooter';
|
||||
|
||||
const DrawerTitle = React.forwardRef<
|
||||
React.ElementRef<typeof DrawerPrimitive.Title>,
|
||||
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Title>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DrawerPrimitive.Title
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'text-lg font-semibold leading-none tracking-tight',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DrawerTitle.displayName = DrawerPrimitive.Title.displayName;
|
||||
|
||||
const DrawerDescription = React.forwardRef<
|
||||
React.ElementRef<typeof DrawerPrimitive.Description>,
|
||||
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Description>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DrawerPrimitive.Description
|
||||
ref={ref}
|
||||
className={cn('text-sm text-muted-foreground', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
DrawerDescription.displayName = DrawerPrimitive.Description.displayName;
|
||||
|
||||
export {
|
||||
Drawer,
|
||||
DrawerPortal,
|
||||
DrawerOverlay,
|
||||
DrawerTrigger,
|
||||
DrawerClose,
|
||||
DrawerContent,
|
||||
DrawerHeader,
|
||||
DrawerFooter,
|
||||
DrawerTitle,
|
||||
DrawerDescription,
|
||||
};
|
||||
@@ -14,6 +14,10 @@ export interface LayoutContext {
|
||||
|
||||
selectedSidebarSection: SidebarSection;
|
||||
selectSidebarSection: (section: SidebarSection) => void;
|
||||
|
||||
isSidePanelShowed: boolean;
|
||||
hideSidePanel: () => void;
|
||||
showSidePanel: () => void;
|
||||
}
|
||||
|
||||
export const layoutContext = createContext<LayoutContext>({
|
||||
@@ -27,4 +31,8 @@ export const layoutContext = createContext<LayoutContext>({
|
||||
selectSidebarSection: emptyFn,
|
||||
openTableFromSidebar: emptyFn,
|
||||
closeAllTablesInSidebar: emptyFn,
|
||||
|
||||
isSidePanelShowed: false,
|
||||
hideSidePanel: emptyFn,
|
||||
showSidePanel: emptyFn,
|
||||
});
|
||||
|
||||
@@ -11,12 +11,20 @@ export const LayoutProvider: React.FC<React.PropsWithChildren> = ({
|
||||
React.useState<string | undefined>();
|
||||
const [selectedSidebarSection, setSelectedSidebarSection] =
|
||||
React.useState<SidebarSection>('tables');
|
||||
const [isSidePanelShowed, setIsSidePanelShowed] =
|
||||
React.useState<boolean>(false);
|
||||
|
||||
const closeAllTablesInSidebar: LayoutContext['closeAllTablesInSidebar'] =
|
||||
() => setOpenedTableInSidebar('');
|
||||
|
||||
const closeAllRelationshipsInSidebar: LayoutContext['closeAllRelationshipsInSidebar'] =
|
||||
() => setOpenedRelationshipInSidebar('');
|
||||
|
||||
const hideSidePanel: LayoutContext['hideSidePanel'] = () =>
|
||||
setIsSidePanelShowed(false);
|
||||
|
||||
const showSidePanel: LayoutContext['showSidePanel'] = () =>
|
||||
setIsSidePanelShowed(true);
|
||||
return (
|
||||
<layoutContext.Provider
|
||||
value={{
|
||||
@@ -28,6 +36,9 @@ export const LayoutProvider: React.FC<React.PropsWithChildren> = ({
|
||||
openRelationshipFromSidebar: setOpenedRelationshipInSidebar,
|
||||
closeAllTablesInSidebar,
|
||||
closeAllRelationshipsInSidebar,
|
||||
isSidePanelShowed,
|
||||
hideSidePanel,
|
||||
showSidePanel,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -22,6 +22,10 @@ import { useChartDB } from '@/hooks/use-chartdb';
|
||||
import { LEFT_HANDLE_ID_PREFIX, TARGET_ID_PREFIX } from './table-node-field';
|
||||
import { Toolbar } from './toolbar/toolbar';
|
||||
import { useToast } from '@/components/toast/use-toast';
|
||||
import { Pencil } from 'lucide-react';
|
||||
import { Button } from '@/components/button/button';
|
||||
import { useLayout } from '@/hooks/use-layout';
|
||||
import { useBreakpoint } from '@/hooks/use-breakpoint';
|
||||
|
||||
type AddEdgeParams = Parameters<typeof addEdge<TableEdgeType>>[0];
|
||||
|
||||
@@ -41,6 +45,8 @@ export const Canvas: React.FC<CanvasProps> = () => {
|
||||
removeRelationships,
|
||||
getField,
|
||||
} = useChartDB();
|
||||
const { showSidePanel } = useLayout();
|
||||
const { isMd: isDesktop } = useBreakpoint('md');
|
||||
const nodeTypes = useMemo(() => ({ table: TableNode }), []);
|
||||
const edgeTypes = useMemo(() => ({ 'table-edge': TableEdge }), []);
|
||||
|
||||
@@ -250,6 +256,23 @@ export const Canvas: React.FC<CanvasProps> = () => {
|
||||
}}
|
||||
panOnScroll
|
||||
>
|
||||
{!isDesktop ? (
|
||||
<Controls
|
||||
position="bottom-left"
|
||||
orientation="horizontal"
|
||||
showZoom={false}
|
||||
showFitView={false}
|
||||
showInteractive={false}
|
||||
className="!shadow-none"
|
||||
>
|
||||
<Button
|
||||
className="bg-pink-600 hover:bg-pink-500 w-11 h-11 p-2"
|
||||
onClick={showSidePanel}
|
||||
>
|
||||
<Pencil />
|
||||
</Button>
|
||||
</Controls>
|
||||
) : null}
|
||||
<Controls
|
||||
position="bottom-center"
|
||||
orientation="horizontal"
|
||||
|
||||
@@ -15,9 +15,19 @@ import { useRedoUndoStack } from '@/hooks/use-redo-undo-stack';
|
||||
import { Toaster } from '@/components/toast/toaster';
|
||||
import { useFullScreenLoader } from '@/hooks/use-full-screen-spinner';
|
||||
import { useBreakpoint } from '@/hooks/use-breakpoint';
|
||||
import { useLayout } from '@/hooks/use-layout';
|
||||
import {
|
||||
Drawer,
|
||||
DrawerContent,
|
||||
DrawerDescription,
|
||||
DrawerHeader,
|
||||
DrawerTitle,
|
||||
} from '@/components/drawer/drawer';
|
||||
import { Separator } from '@/components/separator/separator';
|
||||
|
||||
export const EditorPage: React.FC = () => {
|
||||
const { loadDiagram, currentDiagram } = useChartDB();
|
||||
const { isSidePanelShowed, hideSidePanel } = useLayout();
|
||||
const { resetRedoStack, resetUndoStack } = useRedoUndoStack();
|
||||
const { showLoader, hideLoader } = useFullScreenLoader();
|
||||
const { openCreateDiagramDialog } = useDialog();
|
||||
@@ -26,6 +36,7 @@ export const EditorPage: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const { isLg } = useBreakpoint('lg');
|
||||
const { isXl } = useBreakpoint('xl');
|
||||
const { isMd: isDesktop } = useBreakpoint('md');
|
||||
|
||||
useEffect(() => {
|
||||
if (!config) {
|
||||
@@ -68,21 +79,46 @@ export const EditorPage: React.FC = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<section className="bg-background h-screen w-screen flex flex-col">
|
||||
<section
|
||||
className={`bg-background ${isDesktop ? 'h-screen w-screen' : 'h-dvh w-dvw'} flex flex-col overflow-x-hidden`}
|
||||
>
|
||||
<TopNavbar />
|
||||
{isDesktop ? (
|
||||
<ResizablePanelGroup direction="horizontal">
|
||||
<ResizablePanel
|
||||
defaultSize={isXl ? 25 : isLg ? 35 : 50}
|
||||
minSize={isXl ? 25 : isLg ? 35 : 50}
|
||||
maxSize={99}
|
||||
maxSize={!isSidePanelShowed ? 99 : 0}
|
||||
>
|
||||
<SidePanel />
|
||||
</ResizablePanel>
|
||||
<ResizableHandle />
|
||||
<ResizablePanel defaultSize={isXl ? 75 : isLg ? 65 : 50}>
|
||||
<ResizablePanel
|
||||
defaultSize={isXl ? 75 : isLg ? 65 : 50}
|
||||
>
|
||||
<Canvas />
|
||||
</ResizablePanel>
|
||||
</ResizablePanelGroup>
|
||||
) : (
|
||||
<>
|
||||
<Drawer
|
||||
open={isSidePanelShowed}
|
||||
onClose={() => hideSidePanel()}
|
||||
>
|
||||
<DrawerContent className="h-full">
|
||||
<DrawerHeader>
|
||||
<DrawerTitle>Manage Diagram</DrawerTitle>
|
||||
<DrawerDescription>
|
||||
Manage your diagram objects
|
||||
</DrawerDescription>
|
||||
</DrawerHeader>
|
||||
<Separator orientation="horizontal" />
|
||||
<SidePanel />
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
<Canvas />
|
||||
</>
|
||||
)}
|
||||
</section>
|
||||
<Toaster />
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user