mirror of
https://github.com/Shadowfita/docmost.git
synced 2025-11-17 18:21:04 +10:00
Add sidebar
This commit is contained in:
37
frontend/src/components/sidebar/actions/sidebar-actions.tsx
Normal file
37
frontend/src/components/sidebar/actions/sidebar-actions.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
import { ReactNode } from 'react';
|
||||
import {
|
||||
IconHome,
|
||||
IconSearch,
|
||||
IconSettings,
|
||||
IconFilePlus,
|
||||
} from '@tabler/icons-react';
|
||||
|
||||
export type NavigationMenuType = {
|
||||
label: string;
|
||||
path: string;
|
||||
icon: ReactNode;
|
||||
isActive?: boolean;
|
||||
onClick?: React.MouseEventHandler<HTMLAnchorElement | HTMLButtonElement>;
|
||||
};
|
||||
export const navigationMenu: NavigationMenuType[] = [
|
||||
{
|
||||
label: 'Home',
|
||||
path: '',
|
||||
icon: <IconHome size={16} />,
|
||||
},
|
||||
{
|
||||
label: 'Search',
|
||||
path: '',
|
||||
icon: <IconSearch size={16} />,
|
||||
},
|
||||
{
|
||||
label: 'Settings',
|
||||
path: '',
|
||||
icon: <IconSettings size={16} />,
|
||||
},
|
||||
{
|
||||
label: 'New Page',
|
||||
path: '',
|
||||
icon: <IconFilePlus size={16} />,
|
||||
},
|
||||
];
|
||||
5
frontend/src/components/sidebar/atoms/sidebar-atom.ts
Normal file
5
frontend/src/components/sidebar/atoms/sidebar-atom.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { atomWithWebStorage } from "@/lib/jotai-helper";
|
||||
import { atom } from "jotai";
|
||||
|
||||
export const desktopSidebarAtom = atomWithWebStorage('showSidebar',true);
|
||||
export const mobileSidebarAtom = atom(false);
|
||||
@ -0,0 +1,8 @@
|
||||
import { useAtom } from "jotai";
|
||||
|
||||
export function useToggleSidebar(sidebarAtom) {
|
||||
const [sidebarState, setSidebarState] = useAtom(sidebarAtom);
|
||||
return () => {
|
||||
setSidebarState(!sidebarState);
|
||||
}
|
||||
}
|
||||
15
frontend/src/components/sidebar/sidebar-section.tsx
Normal file
15
frontend/src/components/sidebar/sidebar-section.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import React, { ReactNode } from "react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface SidebarSectionProps {
|
||||
className?: string;
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
export function SidebarSection({className, children}: SidebarSectionProps) {
|
||||
return (
|
||||
<div className={cn('flex-shrink-0 flex-grow-0 pb-0.5', className)}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
62
frontend/src/components/sidebar/sidebar.tsx
Normal file
62
frontend/src/components/sidebar/sidebar.tsx
Normal file
@ -0,0 +1,62 @@
|
||||
import { useIsMobile } from '@/hooks/use-is-mobile';
|
||||
import { useAtom } from 'jotai';
|
||||
import {
|
||||
desktopSidebarAtom,
|
||||
mobileSidebarAtom,
|
||||
} from '@/components/sidebar/atoms/sidebar-atom';
|
||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
import { IconFileText } from '@tabler/icons-react';
|
||||
import { SidebarSection } from '@/components/sidebar/sidebar-section';
|
||||
import {
|
||||
navigationMenu,
|
||||
NavigationMenuType,
|
||||
} from '@/components/sidebar/actions/sidebar-actions';
|
||||
import ButtonWithIcon from '@/components/ui/button-with-icon';
|
||||
|
||||
export default function Sidebar() {
|
||||
const isMobile = useIsMobile();
|
||||
const [isSidebarOpen] = useAtom(
|
||||
isMobile ? mobileSidebarAtom : desktopSidebarAtom
|
||||
);
|
||||
|
||||
return (
|
||||
<nav
|
||||
className={`${
|
||||
isSidebarOpen ? 'w-[270px]' : 'w-[0px]'
|
||||
} flex-grow-0 flex-shrink-0 overflow-hidden border-r duration-300 ease-in-out`}
|
||||
>
|
||||
<div className="flex flex-col flex-shrink-0 gap-0.5 p-[10px]">
|
||||
<div className="h-full">
|
||||
<div className="mt-[20px]"></div>
|
||||
|
||||
<SidebarSection className="pb-2 mb-4 select-none border-b">
|
||||
{navigationMenu.map((menu: NavigationMenuType, index: number) => (
|
||||
<ButtonWithIcon
|
||||
key={index}
|
||||
icon={menu.icon}
|
||||
variant={'ghost'}
|
||||
className="w-full flex flex-1 justify-start items-center"
|
||||
>
|
||||
<span className="text-ellipsis overflow-hidden">
|
||||
{menu.label}
|
||||
</span>
|
||||
</ButtonWithIcon>
|
||||
))}
|
||||
</SidebarSection>
|
||||
|
||||
<ScrollArea className="h-[70vh]">
|
||||
<div className="space-y-1">
|
||||
<ButtonWithIcon
|
||||
variant="ghost"
|
||||
className="w-full justify-start"
|
||||
icon={<IconFileText size={16} />}
|
||||
>
|
||||
Welcome page
|
||||
</ButtonWithIcon>
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
38
frontend/src/components/sidebar/topbar.tsx
Normal file
38
frontend/src/components/sidebar/topbar.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
'use client';
|
||||
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import { useIsMobile } from '@/hooks/use-is-mobile';
|
||||
import {
|
||||
desktopSidebarAtom,
|
||||
mobileSidebarAtom,
|
||||
} from '@/components/sidebar/atoms/sidebar-atom';
|
||||
import { useToggleSidebar } from './hooks/use-toggle-sidebar';
|
||||
import ButtonWithIcon from '../ui/button-with-icon';
|
||||
import { IconLayoutSidebarLeftCollapse } from '@tabler/icons-react';
|
||||
|
||||
export default function TopBar() {
|
||||
const isMobile = useIsMobile();
|
||||
const sidebarStateAtom = isMobile ? mobileSidebarAtom : desktopSidebarAtom;
|
||||
|
||||
const toggleSidebar = useToggleSidebar(sidebarStateAtom);
|
||||
|
||||
return (
|
||||
<header className="max-w-full z-50 select-none">
|
||||
<div
|
||||
className="w-full max-w-full h-[50px] opacity-100 relative
|
||||
transition-opacity duration-700 ease-in transition-color duration-700 ease-in"
|
||||
>
|
||||
<div className="flex justify-between items-center h-full overflow-hidden py-0 px-1 gap-2.5 border-b">
|
||||
<div className="flex items-center leading-tight h-full flex-grow-0 mr-[8px] min-w-0 font-semibold text-sm">
|
||||
<ButtonWithIcon
|
||||
icon={<IconLayoutSidebarLeftCollapse size={20} />}
|
||||
variant={'ghost'}
|
||||
onClick={toggleSidebar}
|
||||
></ButtonWithIcon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user