mirror of
https://github.com/colanode/colanode.git
synced 2025-12-16 11:47:47 +01:00
Redesign sidebar
This commit is contained in:
@@ -57,6 +57,8 @@ const createWindow = async () => {
|
||||
preload: path.join(__dirname, 'preload.js'),
|
||||
},
|
||||
autoHideMenuBar: true,
|
||||
titleBarStyle: 'hiddenInset',
|
||||
trafficLightPosition: { x: 5, y: 5 },
|
||||
});
|
||||
|
||||
mainWindow.setMenuBarVisibility(false);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Plus } from 'lucide-react';
|
||||
import { SquarePen } from 'lucide-react';
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
@@ -20,7 +20,7 @@ export const ChatCreatePopover = () => {
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen} modal={true}>
|
||||
<PopoverTrigger asChild>
|
||||
<Plus className="mr-2 size-4 cursor-pointer" />
|
||||
<SquarePen className="mr-2 size-4 cursor-pointer" />
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-96 p-1">
|
||||
<UserSearch
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
import { Bell, Check, ChevronsUpDown, Plus, Settings } from 'lucide-react';
|
||||
import React from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { Avatar } from '@/renderer/components/avatars/avatar';
|
||||
import { ReadStateIndicator } from '@/renderer/components/layouts/read-state-indicator';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/renderer/components/ui/dropdown-menu';
|
||||
import { useAccount } from '@/renderer/contexts/account';
|
||||
import { useRadar } from '@/renderer/contexts/radar';
|
||||
import { useWorkspace } from '@/renderer/contexts/workspace';
|
||||
import { useQuery } from '@/renderer/hooks/use-query';
|
||||
|
||||
export const LayoutSidebarHeader = () => {
|
||||
const workspace = useWorkspace();
|
||||
const account = useAccount();
|
||||
const navigate = useNavigate();
|
||||
const radar = useRadar();
|
||||
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const { data } = useQuery({
|
||||
type: 'workspace_list',
|
||||
accountId: account.id,
|
||||
});
|
||||
|
||||
const workspaces = data ?? [];
|
||||
const otherWorkspaces = workspaces.filter((w) => w.id !== workspace.id);
|
||||
const otherWorkspaceStates = otherWorkspaces.map((w) =>
|
||||
radar.getWorkspaceState(w.accountId, w.id)
|
||||
);
|
||||
const importantCount = otherWorkspaceStates.reduce(
|
||||
(acc, curr) => acc + curr.importantCount,
|
||||
0
|
||||
);
|
||||
const hasUnseenChanges = otherWorkspaceStates.some((w) => w.hasUnseenChanges);
|
||||
|
||||
return (
|
||||
<div className="p-2">
|
||||
<DropdownMenu open={open} onOpenChange={setOpen}>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<button className="flex w-full items-center gap-2 rounded-md p-2 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground h-10 text-sm focus-visible:outline-none focus-visible:ring-0">
|
||||
<Avatar
|
||||
id={workspace.id}
|
||||
avatar={workspace.avatar}
|
||||
name={workspace.name}
|
||||
className="size-7 rounded-lg"
|
||||
/>
|
||||
<p className="flex-1 text-left text-sm leading-tight truncate font-semibold">
|
||||
{workspace.name}
|
||||
</p>
|
||||
<ChevronsUpDown className="ml-auto size-4" />
|
||||
<ReadStateIndicator
|
||||
count={importantCount}
|
||||
hasChanges={hasUnseenChanges}
|
||||
/>
|
||||
</button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
className="min-w-80 rounded-lg"
|
||||
align="start"
|
||||
side="right"
|
||||
sideOffset={4}
|
||||
>
|
||||
<DropdownMenuItem key={workspace.id} className="p-0">
|
||||
<div className="w-full flex items-center gap-2 px-1 py-1.5 text-left text-sm">
|
||||
<Avatar
|
||||
className="h-8 w-8 rounded-lg"
|
||||
id={workspace.id}
|
||||
name={workspace.name}
|
||||
avatar={workspace.avatar}
|
||||
/>
|
||||
<p className="flex-1 text-left text-sm leading-tight truncate font-semibold">
|
||||
{workspace.name}
|
||||
</p>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
className="gap-2 p-2"
|
||||
onClick={() => {
|
||||
workspace.openSettings();
|
||||
}}
|
||||
>
|
||||
<Settings className="size-4" />
|
||||
<p className="font-medium">Settings</p>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem className="gap-2 p-2" disabled={true}>
|
||||
<Bell className="size-4" />
|
||||
<p className="font-medium">Notifications</p>
|
||||
</DropdownMenuItem>
|
||||
{otherWorkspaces.length > 0 && (
|
||||
<React.Fragment>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuLabel className="mb-1">Workspaces</DropdownMenuLabel>
|
||||
{workspaces.map((workspaceItem) => {
|
||||
const workspaceState = radar.getWorkspaceState(
|
||||
workspaceItem.accountId,
|
||||
workspaceItem.id
|
||||
);
|
||||
return (
|
||||
<DropdownMenuItem
|
||||
key={workspaceItem.id}
|
||||
className="p-0"
|
||||
onClick={() => {
|
||||
navigate(`/${account.id}/${workspaceItem.id}`);
|
||||
}}
|
||||
>
|
||||
<div className="w-full flex items-center gap-2 px-1 py-1.5 text-left text-sm">
|
||||
<Avatar
|
||||
className="h-8 w-8 rounded-lg"
|
||||
id={workspaceItem.id}
|
||||
name={workspaceItem.name}
|
||||
avatar={workspaceItem.avatar}
|
||||
/>
|
||||
<p className="flex-1 text-left text-sm leading-tight truncate font-normal">
|
||||
{workspaceItem.name}
|
||||
</p>
|
||||
{workspaceItem.id === workspace.id ? (
|
||||
<Check className="size-4" />
|
||||
) : (
|
||||
<ReadStateIndicator
|
||||
count={workspaceState.importantCount}
|
||||
hasChanges={workspaceState.hasUnseenChanges}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
)}
|
||||
<DropdownMenuSeparator className="my-1" />
|
||||
<DropdownMenuItem
|
||||
className="gap-2 p-2 text-muted-foreground hover:text-foreground"
|
||||
onClick={() => {
|
||||
navigate(`/${account.id}/create`);
|
||||
}}
|
||||
>
|
||||
<Plus className="size-4" />
|
||||
<p className="font-medium">Create workspace</p>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,17 +0,0 @@
|
||||
import { LayoutSidebarChats } from '@/renderer/components/layouts/layout-sidebar-chats';
|
||||
import { LayoutSidebarFooter } from '@/renderer/components/layouts/layout-sidebar-footer';
|
||||
import { LayoutSidebarHeader } from '@/renderer/components/layouts/layout-sidebar-header';
|
||||
import { LayoutSidebarSpaces } from '@/renderer/components/layouts/layout-sidebar-spaces';
|
||||
|
||||
export const LayoutSidebar = () => {
|
||||
return (
|
||||
<div className="flex h-screen min-h-screen max-h-screen w-64 min-w-64 flex-col bg-sidebar text-sidebar-foreground">
|
||||
<LayoutSidebarHeader />
|
||||
<div className="flex min-h-0 flex-1 flex-col gap-2 overflow-auto">
|
||||
<LayoutSidebarSpaces />
|
||||
<LayoutSidebarChats />
|
||||
</div>
|
||||
<LayoutSidebarFooter />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import { LayoutMain } from '@/renderer/components/layouts/layout-main';
|
||||
import { LayoutModal } from '@/renderer/components/layouts/layout-modal';
|
||||
import { LayoutSidebar } from '@/renderer/components/layouts/layout-sidebar';
|
||||
import { Sidebar } from '@/renderer/components/layouts/sidebars/sidebar';
|
||||
|
||||
interface LayoutProps {
|
||||
main?: string | null;
|
||||
@@ -10,7 +10,7 @@ interface LayoutProps {
|
||||
export const Layout = ({ main, modal }: LayoutProps) => {
|
||||
return (
|
||||
<div className="w-screen min-w-screen h-screen min-h-screen flex flex-row">
|
||||
<LayoutSidebar />
|
||||
<Sidebar />
|
||||
<main className="h-full max-h-screen w-full min-w-128 flex-grow overflow-hidden bg-white">
|
||||
{main && <LayoutMain entryId={main} />}
|
||||
</main>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { ChatCreatePopover } from '@/renderer/components/chats/chat-create-popover';
|
||||
import { ChatSidebarItem } from '@/renderer/components/chats/chat-sidebar-item';
|
||||
import { useWorkspace } from '@/renderer/contexts/workspace';
|
||||
import { useQuery } from '@/renderer/hooks/use-query';
|
||||
import { Header } from '@/renderer/components/ui/header';
|
||||
import { useWorkspace } from '@/renderer/contexts/workspace';
|
||||
import { ChatSidebarItem } from '@/renderer/components/chats/chat-sidebar-item';
|
||||
import { ChatCreatePopover } from '@/renderer/components/chats/chat-create-popover';
|
||||
import { cn } from '@/shared/lib/utils';
|
||||
|
||||
export const LayoutSidebarChats = () => {
|
||||
export const SidebarChats = () => {
|
||||
const workspace = useWorkspace();
|
||||
|
||||
const { data } = useQuery({
|
||||
@@ -18,15 +19,13 @@ export const LayoutSidebarChats = () => {
|
||||
const chats = data ?? [];
|
||||
|
||||
return (
|
||||
<div className="group/sidebar-chats flex w-full min-w-0 flex-col p-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70">
|
||||
Chats
|
||||
</div>
|
||||
<div className="text-muted-foreground opacity-0 transition-opacity group-hover/sidebar-chats:opacity-100 flex items-center justify-center p-0">
|
||||
<div className="flex flex-col group/sidebar-spaces h-full px-2">
|
||||
<Header>
|
||||
<p className="font-medium text-muted-foreground flex-grow">Chats</p>
|
||||
<div className="text-muted-foreground opacity-0 transition-opacity group-hover/sidebar-spaces:opacity-100 flex items-center justify-center p-0">
|
||||
<ChatCreatePopover />
|
||||
</div>
|
||||
</div>
|
||||
</Header>
|
||||
<div className="flex w-full min-w-0 flex-col gap-1">
|
||||
{chats.map((item) => (
|
||||
<button
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Check, ChevronsUpDown, LogOut, Plus, Settings } from 'lucide-react';
|
||||
import { Check, LogOut, Plus, Settings } from 'lucide-react';
|
||||
import React from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
@@ -17,7 +17,7 @@ import { useRadar } from '@/renderer/contexts/radar';
|
||||
import { useQuery } from '@/renderer/hooks/use-query';
|
||||
import { AccountReadState } from '@/shared/types/radars';
|
||||
|
||||
export function LayoutSidebarFooter() {
|
||||
export function SidebarMenuFooter() {
|
||||
const account = useAccount();
|
||||
const navigate = useNavigate();
|
||||
const radar = useRadar();
|
||||
@@ -34,32 +34,16 @@ export function LayoutSidebarFooter() {
|
||||
accountStates[accountItem.id] = radar.getAccountState(accountItem.id);
|
||||
}
|
||||
|
||||
const importantCount = Object.values(accountStates).reduce(
|
||||
(acc, curr) => acc + curr.importantCount,
|
||||
0
|
||||
);
|
||||
const hasUnseenChanges = Object.values(accountStates).some(
|
||||
(state) => state.hasUnseenChanges
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="p-2">
|
||||
<DropdownMenu open={open} onOpenChange={setOpen}>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<button className="flex w-full items-center gap-2 rounded-md p-2 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground h-10 text-sm focus-visible:outline-none focus-visible:ring-0">
|
||||
<button className="flex w-full items-center justify-center">
|
||||
<Avatar
|
||||
className="h-7 w-7 rounded-lg"
|
||||
id={account.id}
|
||||
name={account.name}
|
||||
avatar={account.avatar}
|
||||
/>
|
||||
<p className="flex-1 text-left text-sm leading-tight truncate font-semibold">
|
||||
{account.name}
|
||||
</p>
|
||||
<ChevronsUpDown className="ml-auto size-4" />
|
||||
<ReadStateIndicator
|
||||
count={importantCount}
|
||||
hasChanges={hasUnseenChanges}
|
||||
className="size-10 rounded-lg shadow-md"
|
||||
/>
|
||||
</button>
|
||||
</DropdownMenuTrigger>
|
||||
@@ -0,0 +1,134 @@
|
||||
import { Bell, Check, Plus, Settings } from 'lucide-react';
|
||||
import React from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { Avatar } from '@/renderer/components/avatars/avatar';
|
||||
import { ReadStateIndicator } from '@/renderer/components/layouts/read-state-indicator';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/renderer/components/ui/dropdown-menu';
|
||||
import { useAccount } from '@/renderer/contexts/account';
|
||||
import { useRadar } from '@/renderer/contexts/radar';
|
||||
import { useWorkspace } from '@/renderer/contexts/workspace';
|
||||
import { useQuery } from '@/renderer/hooks/use-query';
|
||||
|
||||
export const SidebarMenuHeader = () => {
|
||||
const workspace = useWorkspace();
|
||||
const account = useAccount();
|
||||
const navigate = useNavigate();
|
||||
const radar = useRadar();
|
||||
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const { data } = useQuery({
|
||||
type: 'workspace_list',
|
||||
accountId: account.id,
|
||||
});
|
||||
|
||||
const workspaces = data ?? [];
|
||||
const otherWorkspaces = workspaces.filter((w) => w.id !== workspace.id);
|
||||
|
||||
return (
|
||||
<DropdownMenu open={open} onOpenChange={setOpen}>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<button className="flex w-full items-center justify-center">
|
||||
<Avatar
|
||||
id={workspace.id}
|
||||
avatar={workspace.avatar}
|
||||
name={workspace.name}
|
||||
className="size-10 rounded-lg shadow-md"
|
||||
/>
|
||||
</button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
className="min-w-80 rounded-lg"
|
||||
align="start"
|
||||
side="right"
|
||||
sideOffset={4}
|
||||
>
|
||||
<DropdownMenuItem key={workspace.id} className="p-0">
|
||||
<div className="w-full flex items-center gap-2 px-1 py-1.5 text-left text-sm">
|
||||
<Avatar
|
||||
className="h-8 w-8 rounded-lg"
|
||||
id={workspace.id}
|
||||
name={workspace.name}
|
||||
avatar={workspace.avatar}
|
||||
/>
|
||||
<p className="flex-1 text-left text-sm leading-tight truncate font-semibold">
|
||||
{workspace.name}
|
||||
</p>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
className="gap-2 p-2"
|
||||
onClick={() => {
|
||||
workspace.openSettings();
|
||||
}}
|
||||
>
|
||||
<Settings className="size-4" />
|
||||
<p className="font-medium">Settings</p>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem className="gap-2 p-2" disabled={true}>
|
||||
<Bell className="size-4" />
|
||||
<p className="font-medium">Notifications</p>
|
||||
</DropdownMenuItem>
|
||||
{otherWorkspaces.length > 0 && (
|
||||
<React.Fragment>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuLabel className="mb-1">Workspaces</DropdownMenuLabel>
|
||||
{workspaces.map((workspaceItem) => {
|
||||
const workspaceState = radar.getWorkspaceState(
|
||||
workspaceItem.accountId,
|
||||
workspaceItem.id
|
||||
);
|
||||
return (
|
||||
<DropdownMenuItem
|
||||
key={workspaceItem.id}
|
||||
className="p-0"
|
||||
onClick={() => {
|
||||
navigate(`/${account.id}/${workspaceItem.id}`);
|
||||
}}
|
||||
>
|
||||
<div className="w-full flex items-center gap-2 px-1 py-1.5 text-left text-sm">
|
||||
<Avatar
|
||||
className="h-8 w-8 rounded-lg"
|
||||
id={workspaceItem.id}
|
||||
name={workspaceItem.name}
|
||||
avatar={workspaceItem.avatar}
|
||||
/>
|
||||
<p className="flex-1 text-left text-sm leading-tight truncate font-normal">
|
||||
{workspaceItem.name}
|
||||
</p>
|
||||
{workspaceItem.id === workspace.id ? (
|
||||
<Check className="size-4" />
|
||||
) : (
|
||||
<ReadStateIndicator
|
||||
count={workspaceState.importantCount}
|
||||
hasChanges={workspaceState.hasUnseenChanges}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
)}
|
||||
<DropdownMenuSeparator className="my-1" />
|
||||
<DropdownMenuItem
|
||||
className="gap-2 p-2 text-muted-foreground hover:text-foreground"
|
||||
onClick={() => {
|
||||
navigate(`/${account.id}/create`);
|
||||
}}
|
||||
>
|
||||
<Plus className="size-4" />
|
||||
<p className="font-medium">Create workspace</p>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
import { cn } from '@/shared/lib/utils';
|
||||
|
||||
interface SidebarMenuIconProps {
|
||||
icon: React.ComponentType<{ className?: string }>;
|
||||
onClick: () => void;
|
||||
isActive?: boolean;
|
||||
}
|
||||
|
||||
export const SidebarMenuIcon = ({
|
||||
icon: Icon,
|
||||
onClick,
|
||||
isActive = false,
|
||||
}: SidebarMenuIconProps) => {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'w-10 h-10 flex items-center justify-center hover:cursor-pointer hover:bg-gray-200 rounded-md',
|
||||
isActive ? 'bg-gray-200' : ''
|
||||
)}
|
||||
onClick={onClick}
|
||||
>
|
||||
<Icon
|
||||
className={cn(
|
||||
'size-5',
|
||||
isActive ? 'text-primary' : 'text-muted-foreground'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
import { LayoutGrid, MessageCircle } from 'lucide-react';
|
||||
|
||||
import { SidebarMenuIcon } from '@/renderer/components/layouts/sidebars/sidebar-menu-icon';
|
||||
import { SidebarMenuHeader } from '@/renderer/components/layouts/sidebars/sidebar-menu-header';
|
||||
import { SidebarMenuFooter } from '@/renderer/components/layouts/sidebars/sidebar-menu-footer';
|
||||
|
||||
interface SidebarMenuProps {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
}
|
||||
|
||||
export const SidebarMenu = ({ value, onChange }: SidebarMenuProps) => {
|
||||
return (
|
||||
<div className="flex flex-col h-full w-[65px] min-w-[65px] items-center bg-slate-100">
|
||||
<div className="h-8 w-full app-drag-region"></div>
|
||||
<SidebarMenuHeader />
|
||||
<div className="flex flex-col gap-1 mt-2 w-full p-2 items-center flex-grow">
|
||||
<SidebarMenuIcon
|
||||
icon={MessageCircle}
|
||||
onClick={() => {
|
||||
onChange('chats');
|
||||
}}
|
||||
isActive={value === 'chats'}
|
||||
/>
|
||||
<SidebarMenuIcon
|
||||
icon={LayoutGrid}
|
||||
onClick={() => {
|
||||
onChange('spaces');
|
||||
}}
|
||||
isActive={value === 'spaces'}
|
||||
/>
|
||||
</div>
|
||||
<SidebarMenuFooter />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,9 +1,10 @@
|
||||
import { useQuery } from '@/renderer/hooks/use-query';
|
||||
import { Header } from '@/renderer/components/ui/header';
|
||||
import { useWorkspace } from '@/renderer/contexts/workspace';
|
||||
import { SpaceCreateButton } from '@/renderer/components/spaces/space-create-button';
|
||||
import { SpaceSidebarItem } from '@/renderer/components/spaces/space-sidebar-item';
|
||||
import { useWorkspace } from '@/renderer/contexts/workspace';
|
||||
import { useQuery } from '@/renderer/hooks/use-query';
|
||||
|
||||
export const LayoutSidebarSpaces = () => {
|
||||
export const SidebarSpaces = () => {
|
||||
const workspace = useWorkspace();
|
||||
const canCreateSpace =
|
||||
workspace.role !== 'guest' && workspace.role !== 'none';
|
||||
@@ -20,18 +21,15 @@ export const LayoutSidebarSpaces = () => {
|
||||
const spaces = data ?? [];
|
||||
|
||||
return (
|
||||
<div className="group/sidebar-spaces flex w-full min-w-0 flex-col p-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70">
|
||||
Spaces
|
||||
</div>
|
||||
<div className="flex flex-col group/sidebar-spaces h-full px-2">
|
||||
<Header>
|
||||
<p className="font-medium text-muted-foreground flex-grow">Spaces</p>
|
||||
{canCreateSpace && (
|
||||
<div className="text-muted-foreground opacity-0 transition-opacity group-hover/sidebar-spaces:opacity-100 flex items-center justify-center p-0">
|
||||
<SpaceCreateButton />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
</Header>
|
||||
<div className="flex w-full min-w-0 flex-col gap-1">
|
||||
{spaces.map((space) => (
|
||||
<SpaceSidebarItem space={space} key={space.id} />
|
||||
@@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
|
||||
import { SidebarMenu } from '@/renderer/components/layouts/sidebars/sidebar-menu';
|
||||
import { SidebarChats } from '@/renderer/components/layouts/sidebars/sidebar-chats';
|
||||
import { SidebarSpaces } from '@/renderer/components/layouts/sidebars/sidebar-spaces';
|
||||
|
||||
export const Sidebar = () => {
|
||||
const [menu, setMenu] = React.useState('spaces');
|
||||
|
||||
return (
|
||||
<div className="flex h-screen min-h-screen max-h-screen w-80 min-w-80 flex-row bg-slate-50">
|
||||
<SidebarMenu value={menu} onChange={setMenu} />
|
||||
<div className="min-h-0 flex-grow overflow-auto">
|
||||
{menu === 'spaces' && <SidebarSpaces />}
|
||||
{menu === 'chats' && <SidebarChats />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -105,3 +105,11 @@
|
||||
.cm-editor.cm-focused {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.app-drag-region {
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
|
||||
.app-no-drag-region {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
@@ -389,8 +389,6 @@ const processEmojisIntoDb = (database: SQLite.Database) => {
|
||||
console.log(`Done processing emojis into database.`);
|
||||
};
|
||||
|
||||
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
const generateEmojis = async () => {
|
||||
if (!fs.existsSync(WORK_DIR_PATH)) {
|
||||
fs.mkdirSync(WORK_DIR_PATH);
|
||||
|
||||
@@ -368,8 +368,6 @@ const processIconsIntoDb = (db: SQLite.Database) => {
|
||||
console.log('Done processing icons into database.');
|
||||
};
|
||||
|
||||
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
const generateIcons = async () => {
|
||||
if (!fs.existsSync(WORK_DIR_PATH)) {
|
||||
fs.mkdirSync(WORK_DIR_PATH);
|
||||
|
||||
Reference in New Issue
Block a user