Refactor sidebar layout

This commit is contained in:
Hakan Shehu
2024-11-12 20:00:34 +01:00
parent 35bb64384a
commit 641a0a594f
31 changed files with 295 additions and 208 deletions

View File

@@ -1,8 +1,10 @@
import { NodeCollaboratorsPopover } from '@/renderer/components/collaborators/node-collaborators-popover';
import { ChannelNode, Node, NodeRole } from '@colanode/core';
import { Header } from '@/renderer/components/ui/header';
import { ContainerBreadcrumb } from '@/renderer/components/workspaces/containers/container-breadcrumb';
import { ChannelSettings } from './channel-settings';
import { NodeBreadcrumb } from '@/renderer/components/layouts/node-breadcrumb';
import { ChannelSettings } from '@/renderer/components/channels/channel-settings';
import { useContainer } from '@/renderer/contexts/container';
import { NodeFullscreenButton } from '@/renderer/components/layouts/node-fullscreen-button';
interface ChannelHeaderProps {
nodes: Node[];
@@ -11,10 +13,17 @@ interface ChannelHeaderProps {
}
export const ChannelHeader = ({ nodes, channel }: ChannelHeaderProps) => {
const container = useContainer();
return (
<Header>
<div className="flex w-full items-center gap-2 px-4">
<ContainerBreadcrumb nodes={nodes} />
<div className="flex-grow">
{container.mode === 'main' && <NodeBreadcrumb nodes={nodes} />}
{container.mode === 'modal' && (
<NodeFullscreenButton nodeId={channel.id} />
)}
</div>
<div className="flex items-center gap-2">
<NodeCollaboratorsPopover nodeId={channel.id} nodes={nodes} />
<ChannelSettings nodeId={channel.id} />

View File

@@ -1,17 +1,26 @@
import { NodeCollaboratorsPopover } from '@/renderer/components/collaborators/node-collaborators-popover';
import { ChatNode } from '@colanode/core';
import { Header } from '@/renderer/components/ui/header';
import { ContainerBreadcrumb } from '@/renderer/components/workspaces/containers/container-breadcrumb';
import { NodeBreadcrumb } from '@/renderer/components/layouts/node-breadcrumb';
import { useContainer } from '@/renderer/contexts/container';
import { NodeFullscreenButton } from '@/renderer/components/layouts/node-fullscreen-button';
interface ChatHeaderProps {
chat: ChatNode;
}
export const ChatHeader = ({ chat }: ChatHeaderProps) => {
const container = useContainer();
return (
<Header>
<div className="flex w-full items-center gap-2 px-4">
<ContainerBreadcrumb nodes={[chat]} />
<div className="flex-grow">
<NodeBreadcrumb nodes={[chat]} />
{container.mode === 'modal' && (
<NodeFullscreenButton nodeId={chat.id} />
)}
</div>
<div className="flex items-center gap-2">
<NodeCollaboratorsPopover nodeId={chat.id} nodes={[chat]} />
</div>

View File

@@ -1,8 +1,10 @@
import { NodeCollaboratorsPopover } from '@/renderer/components/collaborators/node-collaborators-popover';
import { DatabaseNode, Node, NodeRole } from '@colanode/core';
import { Header } from '@/renderer/components/ui/header';
import { ContainerBreadcrumb } from '@/renderer/components/workspaces/containers/container-breadcrumb';
import { NodeBreadcrumb } from '@/renderer/components/layouts/node-breadcrumb';
import { DatabaseSettings } from '@/renderer/components/databases/database-settings';
import { useContainer } from '@/renderer/contexts/container';
import { NodeFullscreenButton } from '@/renderer/components/layouts/node-fullscreen-button';
interface DatabaseHeaderProps {
nodes: Node[];
@@ -11,10 +13,17 @@ interface DatabaseHeaderProps {
}
export const DatabaseHeader = ({ nodes, database }: DatabaseHeaderProps) => {
const container = useContainer();
return (
<Header>
<div className="flex w-full items-center gap-2 px-4">
<ContainerBreadcrumb nodes={nodes} />
<div className="flex-grow">
{container.mode === 'main' && <NodeBreadcrumb nodes={nodes} />}
{container.mode === 'modal' && (
<NodeFullscreenButton nodeId={database.id} />
)}
</div>
<div className="flex items-center gap-2">
<NodeCollaboratorsPopover nodeId={database.id} nodes={nodes} />
<DatabaseSettings nodeId={database.id} />

View File

@@ -1,8 +1,10 @@
import { NodeCollaboratorsPopover } from '@/renderer/components/collaborators/node-collaborators-popover';
import { FileNode, Node, NodeRole } from '@colanode/core';
import { Header } from '@/renderer/components/ui/header';
import { ContainerBreadcrumb } from '@/renderer/components/workspaces/containers/container-breadcrumb';
import { NodeBreadcrumb } from '@/renderer/components/layouts/node-breadcrumb';
import { FileSettings } from '@/renderer/components/files/file-settings';
import { useContainer } from '@/renderer/contexts/container';
import { NodeFullscreenButton } from '@/renderer/components/layouts/node-fullscreen-button';
interface FileHeaderProps {
nodes: Node[];
@@ -11,10 +13,17 @@ interface FileHeaderProps {
}
export const FileHeader = ({ nodes, file }: FileHeaderProps) => {
const container = useContainer();
return (
<Header>
<div className="flex w-full items-center gap-2 px-4">
<ContainerBreadcrumb nodes={nodes} />
<div className="flex-grow">
<NodeBreadcrumb nodes={nodes} />
{container.mode === 'modal' && (
<NodeFullscreenButton nodeId={file.id} />
)}
</div>
<div className="flex items-center gap-2">
<NodeCollaboratorsPopover nodeId={file.id} nodes={nodes} />
<FileSettings nodeId={file.id} />

View File

@@ -1,8 +1,10 @@
import { NodeCollaboratorsPopover } from '@/renderer/components/collaborators/node-collaborators-popover';
import { FolderNode, Node, NodeRole } from '@colanode/core';
import { Header } from '@/renderer/components/ui/header';
import { ContainerBreadcrumb } from '@/renderer/components/workspaces/containers/container-breadcrumb';
import { NodeBreadcrumb } from '@/renderer/components/layouts/node-breadcrumb';
import { FolderSettings } from '@/renderer/components/folders/folder-settings';
import { NodeFullscreenButton } from '@/renderer/components/layouts/node-fullscreen-button';
import { useContainer } from '@/renderer/contexts/container';
interface FolderHeaderProps {
nodes: Node[];
@@ -11,10 +13,17 @@ interface FolderHeaderProps {
}
export const FolderHeader = ({ nodes, folder }: FolderHeaderProps) => {
const container = useContainer();
return (
<Header>
<div className="flex w-full items-center gap-2 px-4">
<ContainerBreadcrumb nodes={nodes} />
<div className="flex-grow">
<NodeBreadcrumb nodes={nodes} />
{container.mode === 'modal' && (
<NodeFullscreenButton nodeId={folder.id} />
)}
</div>
<div className="flex items-center gap-2">
<NodeCollaboratorsPopover nodeId={folder.id} nodes={nodes} />
<FolderSettings nodeId={folder.id} />

View File

@@ -0,0 +1,14 @@
import { ContainerContext } from '@/renderer/contexts/container';
import { NodeContainer } from '@/renderer/components/layouts/node-container';
interface LayoutMainProps {
nodeId: string;
}
export const LayoutMain = ({ nodeId }: LayoutMainProps) => {
return (
<ContainerContext.Provider value={{ nodeId, mode: 'main' }}>
<NodeContainer nodeId={nodeId} />
</ContainerContext.Provider>
);
};

View File

@@ -0,0 +1,38 @@
import React from 'react';
import { Dialog, DialogContent } from '@/renderer/components/ui/dialog';
import { ContainerContext } from '@/renderer/contexts/container';
import { NodeContainer } from '@/renderer/components/layouts/node-container';
import { useWorkspace } from '@/renderer/contexts/workspace';
interface LayoutModalProps {
nodeId: string;
}
export const LayoutModal = ({ nodeId }: LayoutModalProps) => {
const workspace = useWorkspace();
const [open, setOpen] = React.useState(true);
React.useEffect(() => {
if (!open) {
workspace.closeModal();
}
}, [open]);
return (
<Dialog
open={open}
onOpenChange={(open) => {
setOpen(open);
}}
>
<DialogContent
className="flex h-[calc(100vh-100px)] max-h-full w-8/12 max-w-full flex-col gap-1 overflow-hidden px-0.5 pt-0 md:w-8/12"
aria-describedby={undefined}
>
<ContainerContext.Provider value={{ nodeId, mode: 'modal' }}>
<NodeContainer nodeId={nodeId} />
</ContainerContext.Provider>
</DialogContent>
</Dialog>
);
};

View File

@@ -0,0 +1,14 @@
import { ContainerContext } from '@/renderer/contexts/container';
import { NodeContainer } from '@/renderer/components/layouts/node-container';
interface LayoutRightProps {
nodeId: string;
}
export const LayoutRight = ({ nodeId }: LayoutRightProps) => {
return (
<ContainerContext.Provider value={{ nodeId, mode: 'panel' }}>
<NodeContainer nodeId={nodeId} />
</ContainerContext.Provider>
);
};

View File

@@ -13,7 +13,7 @@ import {
import { ChatNode } from '@colanode/core';
import { ChatSidebarItem } from '@/renderer/components/chats/chat-sidebar-item';
export const SidebarChats = () => {
export const LayoutSidebarChats = () => {
const workspace = useWorkspace();
const { data } = useQuery({

View File

@@ -23,7 +23,7 @@ import { useAccount } from '@/renderer/contexts/account';
import { ChevronsUpDown, LogOut, Plus, Settings } from 'lucide-react';
import { useApp } from '@/renderer/contexts/app';
export function SidebarFooter() {
export function LayoutSidebarFooter() {
const [open, setOpen] = React.useState(false);
const app = useApp();

View File

@@ -18,7 +18,7 @@ import {
} from '@/renderer/components/ui/sidebar';
import { ChevronsUpDown, Settings, Plus } from 'lucide-react';
export const SidebarHeader = () => {
export const LayoutSidebarHeader = () => {
const workspace = useWorkspace();
const account = useAccount();
const navigate = useNavigate();

View File

@@ -10,7 +10,7 @@ import { SpaceCreateButton } from '@/renderer/components/spaces/space-create-but
import { SpaceNode } from '@colanode/core';
import { SpaceSidebarItem } from '@/renderer/components/spaces/space-sidebar-item';
export const SidebarSpaces = () => {
export const LayoutSidebarSpaces = () => {
const workspace = useWorkspace();
const canCreateSpace =
workspace.role !== 'guest' && workspace.role !== 'none';

View File

@@ -0,0 +1,30 @@
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarHeader,
SidebarRail,
} from '@/renderer/components/ui/sidebar';
import { LayoutSidebarHeader } from '@/renderer/components/layouts/layout-sidebar-header';
import { LayoutSidebarSpaces } from '@/renderer/components/layouts/layout-sidebar-spaces';
import { LayoutSidebarChats } from '@/renderer/components/layouts/layout-sidebar-chats';
import { LayoutSidebarFooter } from '@/renderer/components/layouts/layout-sidebar-footer';
export const LayoutSidebar = () => {
return (
<Sidebar collapsible="icon">
<SidebarHeader>
<LayoutSidebarHeader />
</SidebarHeader>
<SidebarContent>
<LayoutSidebarSpaces />
<LayoutSidebarChats />
</SidebarContent>
<SidebarFooter>
<LayoutSidebarFooter />
</SidebarFooter>
<SidebarRail />
</Sidebar>
);
};

View File

@@ -0,0 +1,26 @@
import { LayoutSidebar } from '@/renderer/components/layouts/layout-sidebar';
import {
SidebarInset,
SidebarProvider,
} from '@/renderer/components/ui/sidebar';
import { LayoutMain } from '@/renderer/components/layouts/layout-main';
import { LayoutModal } from '@/renderer/components/layouts/layout-modal';
interface LayoutProps {
nodeId?: string | null;
modal?: string | null;
}
export const Layout = ({ nodeId, modal }: LayoutProps) => {
return (
<SidebarProvider>
<LayoutSidebar />
<SidebarInset>
<main className="h-full max-h-screen w-full min-w-128 flex-grow overflow-hidden bg-white">
{nodeId && <LayoutMain nodeId={nodeId} />}
</main>
{modal && <LayoutModal nodeId={modal} />}
</SidebarInset>
</SidebarProvider>
);
};

View File

@@ -8,13 +8,11 @@ import { RecordBreadcrumbItem } from '@/renderer/components/records/record-bread
import { FolderBreadcrumbItem } from '@/renderer/components/folders/folder-breadcrumb-item';
import { FileBreadcrumbItem } from '@/renderer/components/files/file-breadcrumb-item';
interface ContainerBreadcrumbItemProps {
interface NodeBreadcrumbItemProps {
node: Node;
}
export const ContainerBreadcrumbItem = ({
node,
}: ContainerBreadcrumbItemProps) => {
export const NodeBreadcrumbItem = ({ node }: NodeBreadcrumbItemProps) => {
switch (node.type) {
case 'space':
return <SpaceBreadcrumbItem node={node} />;

View File

@@ -14,16 +14,16 @@ import {
DropdownMenuTrigger,
} from '@/renderer/components/ui/dropdown-menu';
import { useWorkspace } from '@/renderer/contexts/workspace';
import { ContainerBreadcrumbItem } from '@/renderer/components/workspaces/containers/container-breadcrumb-item';
import { NodeBreadcrumbItem } from '@/renderer/components/layouts/node-breadcrumb-item';
interface ContainerBreadcrumbProps {
interface NodeBreadcrumbProps {
nodes: Node[];
}
const isClickable = (type: string) =>
type !== NodeTypes.Space && type !== NodeTypes.Message;
export const ContainerBreadcrumb = ({ nodes }: ContainerBreadcrumbProps) => {
export const NodeBreadcrumb = ({ nodes }: NodeBreadcrumbProps) => {
const workspace = useWorkspace();
// Show ellipsis if we have more than 3 nodes (first + last two)
@@ -36,7 +36,7 @@ export const ContainerBreadcrumb = ({ nodes }: ContainerBreadcrumbProps) => {
const ellipsisNodes = showEllipsis ? nodes.slice(1, -2) : [];
return (
<Breadcrumb className="flex-grow">
<Breadcrumb>
<BreadcrumbList>
{visibleNodes.map((node, index) => {
const isFirst = index === 0;
@@ -57,7 +57,7 @@ export const ContainerBreadcrumb = ({ nodes }: ContainerBreadcrumbProps) => {
}
}}
>
<ContainerBreadcrumbItem node={node} />
<NodeBreadcrumbItem node={node} />
</BreadcrumbItem>
{showEllipsis && isFirst && (
<React.Fragment>
@@ -89,7 +89,7 @@ export const ContainerBreadcrumb = ({ nodes }: ContainerBreadcrumbProps) => {
: ''
}
>
<ContainerBreadcrumbItem node={ellipsisNode} />
<NodeBreadcrumbItem node={ellipsisNode} />
</BreadcrumbItem>
</DropdownMenuItem>
);

View File

@@ -1,4 +1,3 @@
import { useParams } from 'react-router-dom';
import { PageContainer } from '@/renderer/components/pages/page-container';
import { ChannelContainer } from '@/renderer/components/channels/channel-container';
import { DatabaseContainer } from '@/renderer/components/databases/database-container';
@@ -8,13 +7,13 @@ import { FolderContainer } from '@/renderer/components/folders/folder-container'
import { FileContainer } from '@/renderer/components/files/file-container';
import { getIdType, IdType } from '@colanode/core';
export const Container = () => {
const { nodeId } = useParams<{ nodeId: string }>();
if (!nodeId) {
return null;
}
interface NodeContainerProps {
nodeId: string;
}
export const NodeContainer = ({ nodeId }: NodeContainerProps) => {
const idType = getIdType(nodeId);
switch (idType) {
case IdType.Channel:
return <ChannelContainer nodeId={nodeId} />;

View File

@@ -0,0 +1,19 @@
import { useWorkspace } from '@/renderer/contexts/workspace';
import { Fullscreen } from 'lucide-react';
interface NodeFullscreenButtonProps {
nodeId: string;
}
export const NodeFullscreenButton = ({ nodeId }: NodeFullscreenButtonProps) => {
const workspace = useWorkspace();
return (
<Fullscreen
className="size-5 cursor-pointer text-muted-foreground hover:text-foreground"
onClick={() => {
workspace.navigateToNode(nodeId);
}}
/>
);
};

View File

@@ -7,11 +7,13 @@ import { PageSidebarItem } from '@/renderer/components/pages/page-sidebar-item';
import { DatabaseSidebarItem } from '@/renderer/components/databases/database-sidiebar-item';
import { FolderSidebarItem } from '@/renderer/components/folders/folder-sidebar-item';
interface SidebarItemProps {
interface NodeSidebarItemProps {
node: Node;
}
export const SidebarItem = ({ node }: SidebarItemProps): React.ReactNode => {
export const NodeSidebarItem = ({
node,
}: NodeSidebarItemProps): React.ReactNode => {
switch (node.type) {
case 'space':
return <SpaceSidebarItem node={node} />;

View File

@@ -1,8 +1,10 @@
import { NodeCollaboratorsPopover } from '@/renderer/components/collaborators/node-collaborators-popover';
import { PageNode, Node, NodeRole } from '@colanode/core';
import { Header } from '@/renderer/components/ui/header';
import { ContainerBreadcrumb } from '@/renderer/components/workspaces/containers/container-breadcrumb';
import { NodeBreadcrumb } from '@/renderer/components/layouts/node-breadcrumb';
import { PageSettings } from '@/renderer/components/pages/page-settings';
import { useContainer } from '@/renderer/contexts/container';
import { NodeFullscreenButton } from '@/renderer/components/layouts/node-fullscreen-button';
interface PageHeaderProps {
nodes: Node[];
@@ -11,10 +13,17 @@ interface PageHeaderProps {
}
export const PageHeader = ({ nodes, page }: PageHeaderProps) => {
const container = useContainer();
return (
<Header>
<div className="flex w-full items-center gap-2 px-4">
<ContainerBreadcrumb nodes={nodes} />
<div className="flex-grow">
<NodeBreadcrumb nodes={nodes} />
{container.mode === 'modal' && (
<NodeFullscreenButton nodeId={page.id} />
)}
</div>
<div className="flex items-center gap-2">
<NodeCollaboratorsPopover nodeId={page.id} nodes={nodes} />
<PageSettings nodeId={page.id} />

View File

@@ -1,8 +1,10 @@
import { NodeCollaboratorsPopover } from '@/renderer/components/collaborators/node-collaborators-popover';
import { RecordNode, Node, NodeRole } from '@colanode/core';
import { Header } from '@/renderer/components/ui/header';
import { ContainerBreadcrumb } from '@/renderer/components/workspaces/containers/container-breadcrumb';
import { NodeBreadcrumb } from '@/renderer/components/layouts/node-breadcrumb';
import { RecordSettings } from '@/renderer/components/records/record-settings';
import { useContainer } from '@/renderer/contexts/container';
import { NodeFullscreenButton } from '@/renderer/components/layouts/node-fullscreen-button';
interface RecordHeaderProps {
nodes: Node[];
@@ -11,10 +13,17 @@ interface RecordHeaderProps {
}
export const RecordHeader = ({ nodes, record }: RecordHeaderProps) => {
const container = useContainer();
return (
<Header>
<div className="flex w-full items-center gap-2 px-4">
<ContainerBreadcrumb nodes={nodes} />
<div className="flex-grow">
{container.mode === 'main' && <NodeBreadcrumb nodes={nodes} />}
{container.mode === 'modal' && (
<NodeFullscreenButton nodeId={record.id} />
)}
</div>
<div className="flex items-center gap-2">
<NodeCollaboratorsPopover nodeId={record.id} nodes={nodes} />
<RecordSettings nodeId={record.id} />

View File

@@ -12,7 +12,7 @@ import {
import { ChannelCreateDialog } from '@/renderer/components/channels/channel-create-dialog';
import { PageCreateDialog } from '@/renderer/components/pages/page-create-dialog';
import { DatabaseCreateDialog } from '@/renderer/components/databases/database-create-dialog';
import { SidebarItem } from '@/renderer/components/workspaces/sidebars/sidebar-item';
import { NodeSidebarItem } from '@/renderer/components/layouts/node-sidebar-item';
import {
Collapsible,
CollapsibleContent,
@@ -170,7 +170,7 @@ export const SpaceSidebarItem = ({ node }: SpaceSidebarItemProps) => {
<SidebarMenuSubButton
isActive={workspace.isNodeActive(child.id)}
>
<SidebarItem node={child} />
<NodeSidebarItem node={child} />
</SidebarMenuSubButton>
</SidebarMenuSubItem>
))}

View File

@@ -10,8 +10,6 @@ const DialogTrigger = DialogPrimitive.Trigger;
const DialogPortal = DialogPrimitive.Portal;
const DialogClose = DialogPrimitive.Close;
const DialogOverlay = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
@@ -42,15 +40,29 @@ const DialogContent = React.forwardRef<
{...props}
>
{children}
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
<Cross2Icon className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogPortal>
));
DialogContent.displayName = DialogPrimitive.Content.displayName;
const DialogClose = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Close>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Close>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Close
ref={ref}
className={cn(
'absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground',
className
)}
{...props}
>
<Cross2Icon className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
));
DialogClose.displayName = DialogPrimitive.Close.displayName;
const DialogHeader = ({
className,
...props

View File

@@ -1,30 +0,0 @@
import { match } from 'ts-pattern';
import { ChannelContainer } from '@/renderer/components/channels/channel-container';
import { PageContainer } from '@/renderer/components/pages/page-container';
import { DatabaseContainer } from '@/renderer/components/databases/database-container';
import { RecordContainer } from '@/renderer/components/records/record-container';
import { ChatContainer } from '@/renderer/components/chats/chat-container';
import { FolderContainer } from '@/renderer/components/folders/folder-container';
import { FileContainer } from '@/renderer/components/files/file-container';
import { getIdType, IdType } from '@colanode/core';
interface ModalContentProps {
nodeId: string;
}
export const ModalContent = ({ nodeId }: ModalContentProps) => {
const idType = getIdType(nodeId);
return (
<div className="flex h-full w-full flex-col">
{match(idType)
.with(IdType.Channel, () => <ChannelContainer nodeId={nodeId} />)
.with(IdType.Page, () => <PageContainer nodeId={nodeId} />)
.with(IdType.Database, () => <DatabaseContainer nodeId={nodeId} />)
.with(IdType.Record, () => <RecordContainer nodeId={nodeId} />)
.with(IdType.Chat, () => <ChatContainer nodeId={nodeId} />)
.with(IdType.Folder, () => <FolderContainer nodeId={nodeId} />)
.with(IdType.File, () => <FileContainer nodeId={nodeId} />)
.otherwise(() => null)}
</div>
);
};

View File

@@ -1,20 +0,0 @@
import { useWorkspace } from '@/renderer/contexts/workspace';
import { Fullscreen } from 'lucide-react';
interface ModalHeaderProps {
nodeId: string;
}
export const ModalHeader = ({ nodeId }: ModalHeaderProps) => {
const workspace = useWorkspace();
return (
<div className="flex h-10 min-h-10 items-center justify-between bg-white p-2">
<button
className="cursor-pointer hover:bg-gray-50"
onClick={() => workspace.navigateToNode(nodeId)}
>
<Fullscreen className="size-4" />
</button>
</div>
);
};

View File

@@ -1,37 +0,0 @@
import React from 'react';
import {
Dialog,
DialogContent,
DialogTitle,
} from '@/renderer/components/ui/dialog';
import { ModalHeader } from '@/renderer/components/workspaces/modals/modal-header';
import { ModalContent } from '@/renderer/components/workspaces/modals/modal-content';
interface ModalProps {
nodeId: string;
onClose: () => void;
}
export const Modal = ({ nodeId, onClose }: ModalProps) => {
const [open, setOpen] = React.useState(true);
return (
<Dialog
open={open}
onOpenChange={(open) => {
setOpen(open);
onClose();
}}
>
<DialogContent
className="flex h-[calc(100vh-100px)] max-h-full w-8/12 max-w-full flex-col gap-1 overflow-hidden px-0.5 pt-2 md:w-8/12"
aria-describedby={undefined}
>
<DialogTitle>
<ModalHeader nodeId={nodeId} />
</DialogTitle>
<ModalContent nodeId={nodeId} />
</DialogContent>
</Dialog>
);
};

View File

@@ -1,30 +0,0 @@
import {
Sidebar as SidebarWrapper,
SidebarContent as SidebarContentWrapper,
SidebarFooter as SidebarFooterWrapper,
SidebarHeader as SidebarHeaderWrapper,
SidebarRail as SidebarRailWrapper,
} from '@/renderer/components/ui/sidebar';
import { SidebarHeader } from '@/renderer/components/workspaces/sidebars/sidebar-header';
import { SidebarSpaces } from '@/renderer/components/workspaces/sidebars/sidebar-spaces';
import { SidebarChats } from '@/renderer/components/workspaces/sidebars/sidebar-chats';
import { SidebarFooter } from '@/renderer/components/workspaces/sidebars/sidebar-footer';
export const Sidebar = () => {
return (
<SidebarWrapper collapsible="icon">
<SidebarHeaderWrapper>
<SidebarHeader />
</SidebarHeaderWrapper>
<SidebarContentWrapper>
<SidebarSpaces />
<SidebarChats />
</SidebarContentWrapper>
<SidebarFooterWrapper>
<SidebarFooter />
</SidebarFooterWrapper>
<SidebarRailWrapper />
</SidebarWrapper>
);
};

View File

@@ -1,19 +1,9 @@
import React from 'react';
import { Sidebar } from '@/renderer/components/workspaces/sidebars/sidebar';
import { WorkspaceContext } from '@/renderer/contexts/workspace';
import {
Outlet,
useNavigate,
useParams,
useSearchParams,
} from 'react-router-dom';
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
import { useAccount } from '@/renderer/contexts/account';
import { Modal } from '@/renderer/components/workspaces/modals/modal';
import { WorkspaceSettingsDialog } from '@/renderer/components/workspaces/workspace-settings-dialog';
import {
SidebarInset,
SidebarProvider,
} from '@/renderer/components/ui/sidebar';
import { Layout } from '@/renderer/components/layouts/layout';
export const Workspace = () => {
const { userId, nodeId } = useParams<{ userId: string; nodeId?: string }>();
@@ -35,6 +25,12 @@ export const Workspace = () => {
...workspace,
navigateToNode(nodeId) {
navigate(`/${userId}/${nodeId}`);
if (nodeId === modal) {
setSearchParams((prev) => {
prev.delete('modal');
return prev;
});
}
},
isNodeActive(id) {
return id === nodeId;
@@ -47,6 +43,12 @@ export const Workspace = () => {
};
});
},
closeModal() {
setSearchParams((prev) => {
prev.delete('modal');
return prev;
});
},
openSettings() {
setOpenSettings(true);
},
@@ -60,32 +62,13 @@ export const Workspace = () => {
},
}}
>
<SidebarProvider>
<Sidebar />
<SidebarInset>
<main className="h-full max-h-screen w-full min-w-128 flex-grow overflow-hidden bg-white">
<Outlet />
</main>
{modal && (
<Modal
nodeId={modal}
key={modal}
onClose={() => {
setSearchParams((prev) => {
prev.delete('modal');
return prev;
});
}}
/>
)}
</SidebarInset>
{openSettings && (
<WorkspaceSettingsDialog
open={openSettings}
onOpenChange={setOpenSettings}
/>
)}
</SidebarProvider>
<Layout nodeId={nodeId} modal={modal} />
{openSettings && (
<WorkspaceSettingsDialog
open={openSettings}
onOpenChange={setOpenSettings}
/>
)}
</WorkspaceContext.Provider>
);
};

View File

@@ -0,0 +1,12 @@
import { createContext, useContext } from 'react';
interface ContainerContext {
nodeId: string;
mode: 'main' | 'modal' | 'panel';
}
export const ContainerContext = createContext<ContainerContext>(
{} as ContainerContext
);
export const useContainer = () => useContext(ContainerContext);

View File

@@ -5,6 +5,7 @@ interface WorkspaceContext extends Workspace {
navigateToNode: (nodeId: string) => void;
isNodeActive: (nodeId: string) => boolean;
openModal: (nodeId: string) => void;
closeModal: () => void;
openSettings: () => void;
markAsSeen: (nodeId: string, versionId: string) => void;
}

View File

@@ -8,7 +8,6 @@ import { WorkspaceCreate } from '@/renderer/components/workspaces/workspace-crea
import { Workspace } from '@/renderer/components/workspaces/workspace';
import { WorkspaceRedirect } from '@/renderer/components/workspaces/workspace-redirect';
import { createHashRouter, RouterProvider } from 'react-router-dom';
import { Container } from '@/renderer/components/workspaces/containers/container';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import { Login } from '@/renderer/components/accounts/login';
@@ -33,14 +32,8 @@ const router = createHashRouter([
element: <Login />,
},
{
path: ':userId',
path: ':userId/:nodeId?',
element: <Workspace />,
children: [
{
path: ':nodeId',
element: <Container />,
},
],
},
],
},