Skip to content

Commit

Permalink
Merge pull request #367 from aulianza/refactor/sidebar
Browse files Browse the repository at this point in the history
refactor: sidebar new
  • Loading branch information
aulianza authored Apr 26, 2024
2 parents 51b7672 + fad7a26 commit f0dd6a5
Show file tree
Hide file tree
Showing 14 changed files with 181 additions and 93 deletions.
8 changes: 4 additions & 4 deletions src/__tests__/components/elements/Breakline.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ describe('Breakline', () => {
const breakline = screen.getByTestId('breakline');
expect(breakline).toBeInTheDocument();
expect(breakline).toHaveClass('border-t');
expect(breakline).toHaveClass('dark:border-neutral-700');
expect(breakline).toHaveClass('border-gray-300');
expect(breakline).toHaveClass('dark:border-neutral-800');
expect(breakline).toHaveClass('border-gray-200');
expect(breakline).toHaveClass('my-4');
});

Expand All @@ -21,8 +21,8 @@ describe('Breakline', () => {
const breakline = screen.getByTestId('breakline');
expect(breakline).toBeInTheDocument();
expect(breakline).toHaveClass('border-t');
expect(breakline).toHaveClass('dark:border-neutral-700');
expect(breakline).toHaveClass('border-gray-300');
expect(breakline).toHaveClass('dark:border-neutral-800');
expect(breakline).toHaveClass('border-gray-200');
expect(breakline).toHaveClass('my-4');
expect(breakline).toHaveClass(customClassName);
});
Expand Down
2 changes: 1 addition & 1 deletion src/common/components/elements/Breakline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ type BreaklineProps = {
const Breakline = ({ className = '', ...others }: BreaklineProps) => {
return (
<div
className={`my-4 border-t border-gray-300 dark:border-neutral-700 ${className}`}
className={`my-4 border-t border-gray-200 dark:border-neutral-800 ${className}`}
data-testid='breakline'
{...others}
></div>
Expand Down
29 changes: 29 additions & 0 deletions src/common/components/elements/SearchBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { useContext } from 'react';
import { BiCommand as CommandIcon } from 'react-icons/bi';
import { FiSearch } from 'react-icons/fi';

import { CommandPaletteContext } from '@/common/context/CommandPaletteContext';

const SearchBox = () => {
const { setIsOpen } = useContext(CommandPaletteContext);

const handleOpenCommandPalette = () => setIsOpen(true);

return (
<div className='flex items-center gap-3 rounded-lg border-[1.8px] border-neutral-300 bg-neutral-100 px-3 py-1 text-neutral-500 backdrop-blur dark:border-neutral-700 dark:bg-neutral-900'>
<FiSearch size={28} />
<span
onClick={() => handleOpenCommandPalette()}
className='w-full text-[15px] hover:cursor-text'
>
Search
</span>
<div className='flex items-center gap-0.5 rounded bg-neutral-200 px-1 py-0.5 text-xs dark:bg-neutral-800'>
<CommandIcon className='mt-0.5' />
<span>k</span>
</div>
</div>
);
};

export default SearchBox;
76 changes: 76 additions & 0 deletions src/common/components/elements/ThemeSwitcher.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { Listbox, Transition } from '@headlessui/react';
import { useTheme } from 'next-themes';
import { Fragment, useEffect, useState } from 'react';
import { LuChevronsUpDown } from 'react-icons/lu';
import { MdDarkMode, MdLightMode } from 'react-icons/md';

const ThemeSwitcher = () => {
const { resolvedTheme, setTheme } = useTheme();

const [mounted, setMounted] = useState(false);

const toggleTheme = () =>
setTheme(resolvedTheme === 'light' ? 'dark' : 'light');

useEffect(() => setMounted(true), []);

if (!mounted) return null;

return (
<Listbox>
<div className='relative mt-1 font-sora'>
<Listbox.Button className='group relative w-full cursor-pointer rounded-lg border-[1.8px] bg-white py-2.5 pl-4 pr-10 text-left text-neutral-600 focus:outline-none focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white/75 focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300 dark:border-neutral-800 dark:bg-neutral-900 dark:text-neutral-400 sm:text-[15px]'>
<span className='flex items-center gap-2 truncate'>
{resolvedTheme === 'dark' ? (
<>
<MdDarkMode size={20} />
<span>Dark Mode</span>
</>
) : (
<>
<MdLightMode size={20} />
<span>Light Mode</span>
</>
)}
</span>
<span className='pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2.5'>
<LuChevronsUpDown
className='h-5 w-5 text-neutral-500 transition-all duration-300 group-hover:text-neutral-600 group-hover:dark:text-neutral-400'
aria-hidden='true'
/>
</span>
</Listbox.Button>
<Transition as={Fragment} leaveFrom='opacity-100' leaveTo='opacity-0'>
<Listbox.Options className='absolute mt-1.5 max-h-60 w-full overflow-auto rounded-md border border-neutral-200 bg-white py-1 text-base ring-1 ring-black/5 focus:outline-none dark:border-neutral-800 dark:bg-neutral-900 sm:text-sm'>
<Listbox.Option
className='relative cursor-pointer select-none py-2 pl-11 pr-4 text-neutral-600 hover:text-neutral-700 dark:text-neutral-400 hover:dark:text-neutral-300'
value={`theme-${resolvedTheme}`}
onClick={toggleTheme}
>
{({ selected }) => (
<>
<span
className={`block truncate ${
selected ? 'font-medium' : 'font-normal'
}`}
>
{resolvedTheme === 'dark' ? 'Light Mode' : 'Dark Mode'}
</span>
<span className='absolute inset-y-0 left-0 flex items-center pl-4'>
{resolvedTheme === 'dark' ? (
<MdLightMode size={20} />
) : (
<MdDarkMode size={20} />
)}
</span>
</>
)}
</Listbox.Option>
</Listbox.Options>
</Transition>
</div>
</Listbox>
);
};

export default ThemeSwitcher;
6 changes: 3 additions & 3 deletions src/common/components/layouts/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const Layout = ({ children }: LayoutProps) => {
{/* <TopBar /> */}
<div
className={clsx(
'mx-auto max-w-6xl lg:px-8',
'mx-auto max-w-6xl',
isDarkTheme ? 'dark:text-darkText' : '',
)}
>
Expand All @@ -53,9 +53,9 @@ const Layout = ({ children }: LayoutProps) => {
<main className='transition-all duration-300'>{children}</main>
</div>
) : (
<div className='flex flex-col lg:flex-row lg:gap-5 lg:py-4 xl:pb-8'>
<div className='flex flex-col lg:flex-row lg:gap-2 lg:py-4 xl:pb-8'>
<HeaderSidebar />
<main className='max-w-[854px] transition-all duration-300 lg:w-4/5'>
<main className='transition-all duration-300 lg:w-4/5'>
{children}
</main>
</div>
Expand Down
23 changes: 16 additions & 7 deletions src/common/components/layouts/partials/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { useEffect, useState } from 'react';

import useIsMobile from '@/common/hooks/useIsMobile';

import Copyright from './Copyright';
import Breakline from '../../elements/Breakline';
import SearchBox from '../../elements/SearchBox';
import ThemeSwitcher from '../../elements/ThemeSwitcher';
import Navigation from '../../sidebar/Navigation';
import Profile from '../../sidebar/Profile';

Expand All @@ -28,16 +29,24 @@ const Sidebar = () => {
return (
<div
id='sidebar'
className='sticky top-0 z-10 flex flex-col transition-all duration-300 lg:py-8'
// className='flex flex-col space-y-6 transition-all duration-300 lg:py-8'
className='sticky top-0 z-10 flex flex-col space-y-6 transition-all duration-300 lg:py-6'
>
<Profile isScrolled={isScrolled} />
{!isMobile && (
<>
<Breakline />
<div className='space-y-3'>
<div className='pb-1'>
<SearchBox />
</div>
<Navigation />
<Breakline className='mt-2' />
<Copyright />
</>
<Breakline className='mx-1' />
<div className='space-y-2.5 px-1'>
<div className='px-3'>
<span className='text-sm text-neutral-600'>Theme</span>
</div>
<ThemeSwitcher />
</div>
</div>
)}
</div>
);
Expand Down
4 changes: 2 additions & 2 deletions src/common/components/sidebar/MenuItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ const MenuItem = ({
const isHashLink = href === '#';
const router = useRouter();

const activeClasses = `flex font-sora items-center gap-2 py-2 px-4 text-neutral-700 dark:text-neutral-400 hover:text-neutral-900 hover:dark:text-neutral-300 rounded-lg group ${
const activeClasses = `flex font-sora items-center gap-2 py-2 pl-4 pr-2.5 text-neutral-700 dark:text-neutral-400 hover:text-neutral-900 hover:dark:text-neutral-300 rounded-lg group ${
router.pathname === href
? 'bg-neutral-200 dark:bg-neutral-800 text-neutral-900 dark:!text-neutral-200'
: 'hover:dark:lg:bg-neutral-800 hover:dark:!text-neutral-300 hover:lg:bg-neutral-200 hover:lg:rounded-lg lg:hover:scale-105 lg:transition-all lg:duration-300'
: 'hover:dark:lg:bg-neutral-800 hover:dark:!text-neutral-300 hover:lg:bg-neutral-200 hover:lg:rounded-lg lg:transition-all lg:duration-300'
}`;

const handleClick = () => {
Expand Down
6 changes: 1 addition & 5 deletions src/common/components/sidebar/MobileMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { motion } from 'framer-motion';
import { FC } from 'react';

import Navigation from './Navigation';
import Breakline from '../elements/Breakline';

const MobileMenu: FC = () => {
return (
Expand All @@ -12,10 +11,7 @@ const MobileMenu: FC = () => {
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3 }}
>
<div>
<Breakline className='mt-2' />
<Navigation />
</div>
<Navigation />
</motion.div>
);
};
Expand Down
47 changes: 13 additions & 34 deletions src/common/components/sidebar/Navigation.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,23 @@
import { useContext } from 'react';
import { BiCommand as CommandIcon } from 'react-icons/bi';
import { useWindowSize } from 'usehooks-ts';

import { MENU_ITEMS } from '@/common/constant/menu';
import { CommandPaletteContext } from '@/common/context/CommandPaletteContext';
import { MENU_APPS, MENU_ITEMS } from '@/common/constant/menu';

import Menu from './Menu';
import MenuItem from './MenuItem';
import Breakline from '../elements/Breakline';

const Navigation = () => {
const { setIsOpen } = useContext(CommandPaletteContext);
const { width } = useWindowSize();
const isMobile = width < 480;

const filterdMenu = MENU_ITEMS?.filter((item) => item?.isShow);

const handleOpenCommandPalette = () => {
setIsOpen(true);
};

const cn = 'group-hover:-rotate-12 transition-all duration-300';
const filteredMenu = MENU_ITEMS?.filter((item) => item?.isShow);
const filteredAppsMenu = MENU_APPS?.filter((item) => item?.isShow);

return (
<div>
<Menu list={filterdMenu} />
<Breakline className='mb-2' />
<MenuItem
title={isMobile ? 'Command' : 'cmd + k'}
href='#'
icon={<CommandIcon className={cn} size={20} />}
isExternal={false}
onClick={() => handleOpenCommandPalette()}
>
{/* <div className='relative inline-flex items-center px-2 py-0.5 rounded-full text-[10px] font-medium bg-green-200 text-green-800 '>
<div className='absolute -ml-2 w-[4.9rem] rounded-full h-5 border-2 border-green-300 animate-badge-pulse'></div>
<span>AI Powered</span>
</div> */}
</MenuItem>
</div>
<>
<Menu list={filteredMenu} />
<Breakline className='mx-1' />
<div className='space-y-1'>
<div className='px-4'>
<span className='text-sm text-neutral-600'>Apps</span>
</div>
<Menu list={filteredAppsMenu} />
</div>
</>
);
};

Expand Down
25 changes: 13 additions & 12 deletions src/common/components/sidebar/Profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import useIsMobile from '@/common/hooks/useIsMobile';
import MobileMenu from './MobileMenu';
import MobileMenuButton from './MobileMenuButton';
import ProfileHeader from './ProfileHeader';
import Status from '../elements/Status';
import SearchBox from '../elements/SearchBox';
import ThemeToggleButton from '../elements/ThemeToggleButton';

interface ProfileProps {
Expand All @@ -19,9 +19,9 @@ const Profile = ({ isScrolled = false }: ProfileProps) => {
const isMobile = useIsMobile();

const getImageSize = () => {
let size = isMobile ? 40 : 100;
let size = isMobile ? 40 : 80;
if (!isMobile && isScrolled) {
size = 80;
size = 0;
}
return size;
};
Expand Down Expand Up @@ -52,15 +52,9 @@ const Profile = ({ isScrolled = false }: ProfileProps) => {
expandMenu && 'pb-0',
)}
>
<div className='flex items-start justify-between md:px-2 lg:flex-col lg:space-y-4'>
<div className='flex items-start justify-between lg:flex-col lg:space-y-4'>
<ProfileHeader expandMenu={expandMenu} imageSize={getImageSize()} />

{!isMobile && (
<div className='flex w-full items-center justify-between'>
<Status />
<ThemeToggleButton />
</div>
)}
{/* <ProfileHeader expandMenu={expandMenu} imageSize={55} /> */}

{isMobile && (
<div
Expand All @@ -80,7 +74,14 @@ const Profile = ({ isScrolled = false }: ProfileProps) => {
</div>

{isMobile && (
<AnimatePresence>{expandMenu && <MobileMenu />}</AnimatePresence>
<AnimatePresence>
{expandMenu && (
<div className='space-y-5 pt-6'>
<SearchBox />
<MobileMenu />
</div>
)}
</AnimatePresence>
)}
</div>
</MenuContext.Provider>
Expand Down
29 changes: 14 additions & 15 deletions src/common/components/sidebar/ProfileHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import Link from 'next/link';
import { MdVerified as VerifiedIcon } from 'react-icons/md';

import Image from '../elements/Image';
import Tooltip from '../elements/Tooltip';

interface ProfileHeaderProps {
expandMenu: boolean;
Expand All @@ -15,7 +14,7 @@ const ProfileHeader = ({ expandMenu, imageSize }: ProfileHeaderProps) => {
return (
<div
className={clsx(
'flex w-full flex-grow items-center gap-4 lg:flex-col lg:items-start lg:gap-0.5',
'flex w-full flex-grow items-center gap-4 lg:flex-col lg:items-start lg:gap-0.5 lg:px-2',
expandMenu && 'flex-col !items-start',
)}
>
Expand All @@ -25,21 +24,21 @@ const ProfileHeader = ({ expandMenu, imageSize }: ProfileHeaderProps) => {
width={expandMenu ? 80 : imageSize}
height={expandMenu ? 80 : imageSize}
rounded='rounded-full'
className='rotate-3 border-2 border-neutral-400 dark:border-neutral-600 lg:hover:scale-105'
className='rotate-3 dark:border-neutral-600 lg:hover:scale-105'
/>
<div className='mt-1 flex items-center gap-2 lg:mt-4'>
<Link href='/' passHref>
<h2 className='flex-grow font-sora text-lg font-medium lg:text-xl'>
Ryan Aulia
</h2>
</Link>
<Tooltip title='Verified'>
<>
<div className='mt-1 flex items-center gap-2 lg:mt-4'>
<Link href='/' passHref>
<h2 className='flex-grow font-sora text-lg font-medium lg:text-xl'>
Ryan Aulia
</h2>
</Link>
<VerifiedIcon size={18} className='text-blue-400' />
</Tooltip>
</div>
<div className='hidden font-sora text-sm text-neutral-600 transition-all duration-300 hover:text-neutral-700 dark:text-neutral-500 dark:hover:text-neutral-400 lg:flex'>
@aulianza
</div>
</div>
<div className='hidden font-sora text-sm text-neutral-600 transition-all duration-300 hover:text-neutral-700 dark:text-neutral-500 dark:hover:text-neutral-400 lg:flex'>
@aulianza
</div>
</>
</div>
);
};
Expand Down
Loading

0 comments on commit f0dd6a5

Please sign in to comment.