Fixes all Nav Menu related errors and bugs (#331)

* chore(client): update lucide-react package to version 0.220.0
style(client): change color of MessageHeader component text to gray-500
style(client): change color of nav-close-button to gray-400 and nav-open-button to gray-500
feat(client): add Panel component to replace svg icons in Nav component

* fix: forwardRef errors in Nav Menu

* refactor(SearchBar.jsx): change clearSearch prop destructuring to props destructuring
refactor(SearchBar.jsx): add ref prop to SearchBar component
refactor(getIcon.jsx): remove unused imports
refactor(getIcon.jsx): add nullish coalescing operator to user.name and user.avatar properties

* fix (NavLinks): modals no longer close on nav menu close

* style(ExportModel.jsx): remove unnecessary z-index property from a div element

* style(ExportModel.jsx): remove trailing whitespace in input element

* refactor(Message.jsx): remove unused cancelled variable
fix(Message.jsx): fix error message length exceeding 512 characters
refactor(MenuItem.jsx): remove unused MenuItem component
This commit is contained in:
Danny Avila 2023-05-19 16:02:41 -04:00 committed by GitHub
parent ee2b3e4fb2
commit ec561fcd7f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 230 additions and 165 deletions

View file

@ -1,11 +1,10 @@
import { useEffect } from 'react';
import store from '~/store';
import TrashIcon from '../svg/TrashIcon';
import { Dialog, DialogTrigger } from '../ui/Dialog.tsx';
import { Dialog } from '../ui/Dialog.tsx';
import DialogTemplate from '../ui/DialogTemplate';
import { useClearConversationsMutation } from '~/data-provider';
export default function ClearConvos() {
const ClearConvos = ({ open, onOpenChange}) => {
const { newConversation } = store.useConversation();
const { refreshConversations } = store.useConversations();
const clearConvosMutation = useClearConversationsMutation();
@ -23,13 +22,7 @@ export default function ClearConvos() {
}, [clearConvosMutation.isSuccess]);
return (
<Dialog>
<DialogTrigger asChild>
<button className="flex w-full cursor-pointer items-center gap-3 px-3 py-3 text-sm text-white transition-colors duration-200 hover:bg-gray-700">
<TrashIcon />
Clear conversations
</button>
</DialogTrigger>
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogTemplate
title="Clear conversations"
description="Are you sure you want to clear all conversations? This is irreversible."
@ -41,4 +34,6 @@ export default function ClearConvos() {
/>
</Dialog>
);
}
};
export default ClearConvos;

View file

@ -1,9 +1,9 @@
import React, { useState, useContext } from 'react';
import { forwardRef, useContext } from 'react';
import DarkModeIcon from '../svg/DarkModeIcon';
import LightModeIcon from '../svg/LightModeIcon';
import { ThemeContext } from '~/hooks/ThemeContext';
export default function DarkMode() {
const DarkMode = forwardRef(() => {
const { theme, setTheme } = useContext(ThemeContext);
const clickHandler = () => setTheme(theme === 'dark' ? 'light' : 'dark');
@ -18,4 +18,6 @@ export default function DarkMode() {
{mode}
</button>
);
}
});
export default DarkMode;

View file

@ -1,10 +1,10 @@
import React, { useEffect, useState } from 'react';
import { useEffect, useState } from 'react';
import { useRecoilValue, useRecoilCallback } from 'recoil';
import filenamify from 'filenamify';
import exportFromJSON from 'export-from-json';
import download from 'downloadjs';
import DialogTemplate from '~/components/ui/DialogTemplate.jsx';
import { Dialog, DialogClose, DialogButton } from '~/components/ui/Dialog.tsx';
import { Dialog, DialogButton } from '~/components/ui/Dialog.tsx';
import { Input } from '~/components/ui/Input.tsx';
import { Label } from '~/components/ui/Label.tsx';
import { Checkbox } from '~/components/ui/Checkbox.tsx';

View file

@ -1,4 +1,4 @@
import React, { useState } from 'react';
import { useState, forwardRef } from 'react';
import { useRecoilValue } from 'recoil';
import { Download } from 'lucide-react';
import { cn } from '~/utils/';
@ -7,7 +7,7 @@ import ExportModel from './ExportModel';
import store from '~/store';
export default function ExportConversation() {
const ExportConversation = forwardRef(() => {
const [open, setOpen] = useState(false);
const conversation = useRecoilValue(store.conversation) || {};
@ -37,4 +37,6 @@ export default function ExportConversation() {
<ExportModel open={open} onOpenChange={setOpen} />
</>
);
}
});
export default ExportConversation;

View file

@ -1,8 +1,8 @@
import React from 'react';
import { forwardRef } from 'react';
import LogOutIcon from '../svg/LogOutIcon';
import { useAuthContext } from '~/hooks/AuthContext';
export default function Logout() {
const Logout = forwardRef(() => {
const { user, logout } = useAuthContext();
const handleLogout = () => {
@ -20,4 +20,6 @@ export default function Logout() {
<small>Log out</small>
</button>
);
}
});
export default Logout;

View file

@ -1,20 +1,25 @@
import React from 'react';
import { forwardRef } from 'react';
import { cn } from '~/utils/';
export default function NavLink({ svg, text, clickHandler }) {
const props = {
className:
'flex cursor-pointer items-center gap-3 rounded-md py-3 px-3 text-sm text-white transition-colors duration-200 hover:bg-gray-500/10'
};
const NavLink = forwardRef((props, ref) => {
const { svg, text, clickHandler, className = '' } = props;
const defaultProps = {};
defaultProps.className = cn(
'flex cursor-pointer items-center gap-3 rounded-md py-3 px-3 text-sm text-white transition-colors duration-200 hover:bg-gray-500/10',
className
);
if (clickHandler) {
props.onClick = clickHandler;
console.log('clickHandler: ', clickHandler);
defaultProps.onClick = clickHandler;
}
return (
<a {...props}>
<a {...defaultProps} ref={ref}>
{svg()}
{text}
</a>
);
}
});
export default NavLink;

View file

@ -1,70 +1,111 @@
import { Menu, Transition } from '@headlessui/react';
import { Fragment, useEffect, useRef, useState } from 'react';
import { Fragment, useState } from 'react';
import { useRecoilValue } from 'recoil';
import SearchBar from './SearchBar';
import TrashIcon from '../svg/TrashIcon';
import { Download } from 'lucide-react';
import NavLink from './NavLink';
import ExportModel from './ExportConversation/ExportModel';
import ClearConvos from './ClearConvos';
import DarkMode from './DarkMode';
import Logout from './Logout';
import ExportConversation from './ExportConversation';
import { useAuthContext } from '~/hooks/AuthContext';
import { cn } from '~/utils/';
import DotsIcon from '../svg/DotsIcon';
import store from '~/store';
export default function NavLinks({ clearSearch, isSearchEnabled }) {
const { user, logout } = useAuthContext();
const [showExports, setShowExports] = useState(false);
const [showClearConvos, setShowClearConvos] = useState(false);
const { user } = useAuthContext();
const conversation = useRecoilValue(store.conversation) || {};
const exportable =
conversation?.conversationId &&
conversation?.conversationId !== 'new' &&
conversation?.conversationId !== 'search';
const clickHandler = () => {
if (exportable) setShowExports(true);
};
return (
<Menu as="div" className="group relative">
{({ open }) => (
<>
<Menu.Button
className={cn(
'group-ui-open:bg-gray-800 flex w-full items-center gap-2.5 rounded-md px-3 py-3 text-sm transition-colors duration-200 hover:bg-gray-800',
open ? 'bg-gray-800' : ''
)}
>
<div className="-ml-0.5 h-5 w-5 flex-shrink-0">
<div className="relative flex">
<img
className="rounded-sm"
src={
user?.avatar || `https://avatars.dicebear.com/api/initials/${user?.name}.svg`
}
alt=""
/>
<>
<Menu as="div" className="group relative">
{({ open }) => (
<>
<Menu.Button
className={cn(
'group-ui-open:bg-gray-800 flex w-full items-center gap-2.5 rounded-md px-3 py-3 text-sm transition-colors duration-200 hover:bg-gray-800',
open ? 'bg-gray-800' : ''
)}
>
<div className="-ml-0.5 h-5 w-5 flex-shrink-0">
<div className="relative flex">
<img
className="rounded-sm"
src={
user?.avatar || `https://avatars.dicebear.com/api/initials/${user?.name}.svg`
}
alt=""
/>
</div>
</div>
</div>
<div className="grow overflow-hidden text-ellipsis whitespace-nowrap text-left text-white">
{user?.name || 'USER'}
</div>
<DotsIcon />
</Menu.Button>
<div className="grow overflow-hidden text-ellipsis whitespace-nowrap text-left text-white">
{user?.name || 'USER'}
</div>
<DotsIcon />
</Menu.Button>
<Transition
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items className="absolute bottom-full left-0 z-20 mb-2 w-full translate-y-0 overflow-hidden rounded-md bg-[#050509] py-1.5 opacity-100 outline-none">
<Menu.Item>
{({}) => <>{!!isSearchEnabled && <SearchBar clearSearch={clearSearch} />}</>}
</Menu.Item>
<Menu.Item>{({}) => <ExportConversation />}</Menu.Item>
<div className="my-1.5 h-px bg-white/20" role="none"></div>
<Menu.Item>{({}) => <DarkMode />}</Menu.Item>
<Menu.Item>{({}) => <ClearConvos />}</Menu.Item>
<div className="my-1.5 h-px bg-white/20" role="none"></div>
<Menu.Item>
<Logout />
</Menu.Item>
</Menu.Items>
</Transition>
</>
)}
</Menu>
<Transition
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items className="absolute bottom-full left-0 z-20 mb-2 w-full translate-y-0 overflow-hidden rounded-md bg-[#050509] py-1.5 opacity-100 outline-none">
<Menu.Item>
{!!isSearchEnabled && <SearchBar clearSearch={clearSearch} />}
</Menu.Item>
<Menu.Item>
<NavLink
className={cn(
'flex w-full cursor-pointer items-center gap-3 px-3 py-3 text-sm text-white transition-colors duration-200 hover:bg-gray-700',
exportable ? 'cursor-pointer text-white' : 'cursor-not-allowed text-gray-400'
)}
svg={() => <Download size={16} />}
text="Export conversation"
clickHandler={clickHandler}
/>
</Menu.Item>
<div className="my-1.5 h-px bg-white/20" role="none" />
<Menu.Item>
<DarkMode />
</Menu.Item>
<Menu.Item>
<NavLink
className="flex w-full cursor-pointer items-center gap-3 px-3 py-3 text-sm text-white transition-colors duration-200 hover:bg-gray-700"
svg={() => <TrashIcon />}
text="Clear conversations"
clickHandler={() => setShowClearConvos(true)}
/>
</Menu.Item>
<div className="my-1.5 h-px bg-white/20" role="none" />
<Menu.Item>
<Logout />
</Menu.Item>
</Menu.Items>
</Transition>
</>
)}
</Menu>
{showExports && <ExportModel open={showExports} onOpenChange={setShowExports} />}
{showClearConvos && <ClearConvos open={showClearConvos} onOpenChange={setShowClearConvos} />}
</>
);
}

View file

@ -1,8 +1,10 @@
import { forwardRef } from 'react';
import { Search } from 'lucide-react';
import { useRecoilState } from 'recoil';
import store from '~/store';
export default function SearchBar({ clearSearch }) {
const SearchBar = forwardRef((props, ref) => {
const { clearSearch } = props;
const [searchQuery, setSearchQuery] = useRecoilState(store.searchQuery);
const handleKeyUp = (e) => {
@ -19,7 +21,10 @@ export default function SearchBar({ clearSearch }) {
};
return (
<div className="flex w-full cursor-pointer items-center gap-3 px-3 py-3 text-sm text-white transition-colors duration-200 hover:bg-gray-700">
<div
ref={ref}
className="flex w-full cursor-pointer items-center gap-3 px-3 py-3 text-sm text-white transition-colors duration-200 hover:bg-gray-700"
>
{<Search className="h-4 w-4" />}
<input
type="text"
@ -34,4 +39,6 @@ export default function SearchBar({ clearSearch }) {
/>
</div>
);
}
});
export default SearchBar;

View file

@ -1,5 +1,6 @@
import { useState, useEffect, useRef, useContext } from 'react';
import NewChat from './NewChat';
import Panel from '../svg/Panel';
import Spinner from '../svg/Spinner';
import Pages from '../Conversations/Pages';
import Conversations from '../Conversations';
@ -198,62 +199,21 @@ export default function Nav({ navVisible, setNavVisible }) {
</div>
<button
type="button"
className={cn('nav-close-button -ml-0.5 -mt-2.5 inline-flex h-10 w-10 items-center justify-center rounded-md focus:outline-none focus:ring-white md:-ml-1 md:-mt-2.5', theme === 'dark' ? 'text-gray-500 hover:text-gray-400' : 'text-gray-900 hover:text-gray-600')}
className={cn('nav-close-button -ml-0.5 -mt-2.5 inline-flex h-10 w-10 items-center justify-center rounded-md focus:outline-none focus:ring-white md:-ml-1 md:-mt-2.5', theme === 'dark' ? 'text-gray-500 hover:text-gray-400' : 'text-gray-400 hover:text-gray-500')}
onClick={toggleNavVisible}
>
<span className="sr-only">Close sidebar</span>
<svg
stroke="currentColor"
fill="none"
strokeWidth="1.5"
viewBox="0 0 24 24"
strokeLinecap="round"
strokeLinejoin="round"
className="block h-6 w-6 md:hidden"
height="1em"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<line x1="3" y1="6" x2="15" y2="18" />
<line x1="3" y1="18" x2="15" y2="6" />
</svg>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="hidden h-[26px] w-[26px] md:block"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M15.75 9V5.25A2.25 2.25 0 0013.5 3h-6a2.25 2.25 0 00-2.25 2.25v13.5A2.25 2.25 0 007.5 21h6a2.25 2.25 0 002.25-2.25V15M12 9l-3 3m0 0l3 3m-3-3h12.75"
/>
</svg>
<Panel/>
</button>
</div>
{!navVisible && (
<button
type="button"
className="nav-open-button fixed left-2 top-0.5 z-10 inline-flex h-10 w-10 items-center justify-center rounded-md text-gray-900 hover:text-gray-600 focus:outline-none focus:ring-white dark:text-gray-500 dark:hover:text-gray-400"
className="nav-open-button fixed left-2 top-0.5 z-10 inline-flex h-10 w-10 items-center justify-center rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-white dark:text-gray-500 dark:hover:text-gray-400"
onClick={toggleNavVisible}
>
<span className="sr-only">Open sidebar</span>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="h-6 w-6"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M15.75 9V5.25A2.25 2.25 0 0013.5 3h-6a2.25 2.25 0 00-2.25 2.25v13.5A2.25 2.25 0 007.5 21h6a2.25 2.25 0 002.25-2.25V15m3 0l3-3m0 0l-3-3m3 3H9"
/>
</svg>
<Panel open={true}/>
</button>
)}