convert folders to buttons & folder icons to buttons (accessibility) (#237)

* tabbable folders

* fix spacing
This commit is contained in:
Brad Ullman 2023-03-28 01:29:56 -07:00 committed by GitHub
parent b0c289f7a4
commit a3eb247c3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 123 additions and 92 deletions

View File

@ -104,7 +104,7 @@ export const ConversationComponent: FC<Props> = ({
{(isDeleting || isRenaming) && {(isDeleting || isRenaming) &&
selectedConversation.id === conversation.id && ( selectedConversation.id === conversation.id && (
<div className="visible absolute right-1 z-10 flex text-gray-300"> <div className="absolute right-1 z-10 flex text-gray-300">
<button <button
className="min-w-[20px] p-1 text-neutral-400 hover:text-neutral-100" className="min-w-[20px] p-1 text-neutral-400 hover:text-neutral-100"
onClick={(e) => { onClick={(e) => {
@ -136,7 +136,7 @@ export const ConversationComponent: FC<Props> = ({
{selectedConversation.id === conversation.id && {selectedConversation.id === conversation.id &&
!isDeleting && !isDeleting &&
!isRenaming && ( !isRenaming && (
<div className="visible absolute right-1 z-10 flex text-gray-300"> <div className="absolute right-1 z-10 flex text-gray-300">
<button <button
className="min-w-[20px] p-1 text-neutral-400 hover:text-neutral-100" className="min-w-[20px] p-1 text-neutral-400 hover:text-neutral-100"
onClick={(e) => { onClick={(e) => {

View File

@ -100,37 +100,50 @@ export const ChatFolder: FC<Props> = ({
}, [searchTerm]); }, [searchTerm]);
return ( return (
<div> <>
<div <div className="relative flex items-center">
className={`mb-1 flex cursor-pointer items-center gap-3 rounded-lg px-3 py-2 text-[14px] leading-normal transition-colors duration-200 hover:bg-[#343541]/90`}
onClick={() => setIsOpen(!isOpen)}
onDrop={(e) => handleDrop(e, currentFolder)}
onDragOver={allowDrop}
onDragEnter={highlightDrop}
onDragLeave={removeHighlight}
>
{isOpen ? <IconCaretDown size={16} /> : <IconCaretRight size={16} />}
{isRenaming ? ( {isRenaming ? (
<input <div className="flex w-full items-center gap-3 bg-[#343541]/90 p-3">
className="flex-1 overflow-hidden overflow-ellipsis border-b border-neutral-400 bg-transparent pr-1 text-left text-white outline-none focus:border-neutral-100" {isOpen ? (
type="text" <IconCaretDown size={18} />
value={renameValue} ) : (
onChange={(e) => setRenameValue(e.target.value)} <IconCaretRight size={18} />
onKeyDown={handleEnterDown} )}
autoFocus
/> <input
) : ( className="mr-12 flex-1 overflow-hidden overflow-ellipsis border-neutral-400 bg-transparent text-left text-[12.5px] leading-3 text-white outline-none focus:border-neutral-100"
<div className="flex-1 overflow-hidden overflow-ellipsis whitespace-nowrap pr-1 text-left"> type="text"
{currentFolder.name} value={renameValue}
onChange={(e) => setRenameValue(e.target.value)}
onKeyDown={handleEnterDown}
autoFocus
/>
</div> </div>
) : (
<button
className={`flex w-full cursor-pointer items-center gap-3 rounded-lg p-3 text-sm transition-colors duration-200 hover:bg-[#343541]/90`}
onClick={() => setIsOpen(!isOpen)}
onDrop={(e) => handleDrop(e, currentFolder)}
onDragOver={allowDrop}
onDragEnter={highlightDrop}
onDragLeave={removeHighlight}
>
{isOpen ? (
<IconCaretDown size={18} />
) : (
<IconCaretRight size={18} />
)}
<div className="relative max-h-5 flex-1 overflow-hidden text-ellipsis whitespace-nowrap break-all text-left text-[12.5px] leading-3">
{currentFolder.name}
</div>
</button>
)} )}
{(isDeleting || isRenaming) && ( {(isDeleting || isRenaming) && (
<div className="-ml-2 flex gap-1"> <div className="absolute right-1 z-10 flex text-gray-300">
<IconCheck <button
className="min-w-[20px] text-neutral-400 hover:text-neutral-100" className="min-w-[20px] p-1 text-neutral-400 hover:text-neutral-100"
size={16}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
@ -143,40 +156,43 @@ export const ChatFolder: FC<Props> = ({
setIsDeleting(false); setIsDeleting(false);
setIsRenaming(false); setIsRenaming(false);
}} }}
/> >
<IconCheck size={18} />
<IconX </button>
className="min-w-[20px] text-neutral-400 hover:text-neutral-100" <button
size={16} className="min-w-[20px] p-1 text-neutral-400 hover:text-neutral-100"
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
setIsDeleting(false); setIsDeleting(false);
setIsRenaming(false); setIsRenaming(false);
}} }}
/> >
<IconX size={18} />
</button>
</div> </div>
)} )}
{!isDeleting && !isRenaming && ( {!isDeleting && !isRenaming && (
<div className="ml-2 flex gap-1"> <div className="absolute right-1 z-10 flex text-gray-300">
<IconPencil <button
className="min-w-[20px] text-neutral-400 hover:text-neutral-100" className="min-w-[20px] p-1 text-neutral-400 hover:text-neutral-100"
size={18}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
setIsRenaming(true); setIsRenaming(true);
setRenameValue(currentFolder.name); setRenameValue(currentFolder.name);
}} }}
/> >
<IconPencil size={18} />
<IconTrash </button>
className=" min-w-[20px] text-neutral-400 hover:text-neutral-100" <button
size={18} className="min-w-[20px] p-1 text-neutral-400 hover:text-neutral-100"
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
setIsDeleting(true); setIsDeleting(true);
}} }}
/> >
<IconTrash size={18} />
</button>
</div> </div>
)} )}
</div> </div>
@ -185,7 +201,7 @@ export const ChatFolder: FC<Props> = ({
? conversations.map((conversation, index) => { ? conversations.map((conversation, index) => {
if (conversation.folderId === currentFolder.id) { if (conversation.folderId === currentFolder.id) {
return ( return (
<div key={index} className="ml-5 gap-2 border-l pl-2 pt-2"> <div key={index} className="ml-5 gap-2 border-l pl-2">
<ConversationComponent <ConversationComponent
selectedConversation={selectedConversation} selectedConversation={selectedConversation}
conversation={conversation} conversation={conversation}
@ -199,6 +215,6 @@ export const ChatFolder: FC<Props> = ({
} }
}) })
: null} : null}
</div> </>
); );
}; };

View File

@ -35,7 +35,7 @@ export const ChatFolders: FC<Props> = ({
onUpdateConversation, onUpdateConversation,
}) => { }) => {
return ( return (
<div className="flex w-full flex-col gap-1 pt-2"> <div className="flex w-full flex-col pt-2">
{folders.map((folder, index) => ( {folders.map((folder, index) => (
<ChatFolder <ChatFolder
key={index} key={index}

View File

@ -96,37 +96,49 @@ export const PromptFolder: FC<Props> = ({
}, [searchTerm]); }, [searchTerm]);
return ( return (
<div> <>
<div <div className="relative flex items-center">
className={`mb-1 flex cursor-pointer items-center gap-3 rounded-lg px-3 py-2 text-[14px] leading-normal transition-colors duration-200 hover:bg-[#343541]/90`}
onClick={() => setIsOpen(!isOpen)}
onDrop={(e) => handleDrop(e, currentFolder)}
onDragOver={allowDrop}
onDragEnter={highlightDrop}
onDragLeave={removeHighlight}
>
{isOpen ? <IconCaretDown size={16} /> : <IconCaretRight size={16} />}
{isRenaming ? ( {isRenaming ? (
<input <div className="flex w-full items-center gap-3 bg-[#343541]/90 p-3">
className="flex-1 overflow-hidden overflow-ellipsis border-b border-neutral-400 bg-transparent pr-1 text-left text-white outline-none focus:border-neutral-100" {isOpen ? (
type="text" <IconCaretDown size={18} />
value={renameValue} ) : (
onChange={(e) => setRenameValue(e.target.value)} <IconCaretRight size={18} />
onKeyDown={handleEnterDown} )}
autoFocus <input
/> className="mr-12 flex-1 overflow-hidden overflow-ellipsis border-neutral-400 bg-transparent text-left text-[12.5px] leading-3 text-white outline-none focus:border-neutral-100"
) : ( type="text"
<div className="flex-1 overflow-hidden overflow-ellipsis whitespace-nowrap pr-1 text-left"> value={renameValue}
{currentFolder.name} onChange={(e) => setRenameValue(e.target.value)}
onKeyDown={handleEnterDown}
autoFocus
/>
</div> </div>
) : (
<button
className={`flex w-full cursor-pointer items-center gap-3 rounded-lg p-3 text-sm transition-colors duration-200 hover:bg-[#343541]/90`}
onClick={() => setIsOpen(!isOpen)}
onDrop={(e) => handleDrop(e, currentFolder)}
onDragOver={allowDrop}
onDragEnter={highlightDrop}
onDragLeave={removeHighlight}
>
{isOpen ? (
<IconCaretDown size={18} />
) : (
<IconCaretRight size={18} />
)}
<div className="relative max-h-5 flex-1 overflow-hidden text-ellipsis whitespace-nowrap break-all text-left text-[12.5px] leading-3">
{currentFolder.name}
</div>
</button>
)} )}
{(isDeleting || isRenaming) && ( {(isDeleting || isRenaming) && (
<div className="-ml-2 flex gap-1"> <div className="absolute right-1 z-10 flex text-gray-300">
<IconCheck <button
className="min-w-[20px] text-neutral-400 hover:text-neutral-100" className="min-w-[20px] p-1 text-neutral-400 hover:text-neutral-100"
size={16}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
@ -139,40 +151,43 @@ export const PromptFolder: FC<Props> = ({
setIsDeleting(false); setIsDeleting(false);
setIsRenaming(false); setIsRenaming(false);
}} }}
/> >
<IconCheck size={18} />
<IconX </button>
className="min-w-[20px] text-neutral-400 hover:text-neutral-100" <button
size={16} className="min-w-[20px] p-1 text-neutral-400 hover:text-neutral-100"
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
setIsDeleting(false); setIsDeleting(false);
setIsRenaming(false); setIsRenaming(false);
}} }}
/> >
<IconX size={18} />
</button>
</div> </div>
)} )}
{!isDeleting && !isRenaming && ( {!isDeleting && !isRenaming && (
<div className="ml-2 flex gap-1"> <div className="absolute right-1 z-10 flex text-gray-300">
<IconPencil <button
className="min-w-[20px] text-neutral-400 hover:text-neutral-100" className="min-w-[20px] p-1 text-neutral-400 hover:text-neutral-100"
size={18}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
setIsRenaming(true); setIsRenaming(true);
setRenameValue(currentFolder.name); setRenameValue(currentFolder.name);
}} }}
/> >
<IconPencil size={18} />
<IconTrash </button>
className=" min-w-[20px] text-neutral-400 hover:text-neutral-100" <button
size={18} className="min-w-[20px] p-1 text-neutral-400 hover:text-neutral-100"
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
setIsDeleting(true); setIsDeleting(true);
}} }}
/> >
<IconTrash size={18} />
</button>
</div> </div>
)} )}
</div> </div>
@ -181,7 +196,7 @@ export const PromptFolder: FC<Props> = ({
? prompts.map((prompt, index) => { ? prompts.map((prompt, index) => {
if (prompt.folderId === currentFolder.id) { if (prompt.folderId === currentFolder.id) {
return ( return (
<div key={index} className="ml-5 gap-2 border-l pl-2 pt-2"> <div key={index} className="ml-5 gap-2 border-l pl-2">
<PromptComponent <PromptComponent
prompt={prompt} prompt={prompt}
onDeletePrompt={onDeletePrompt} onDeletePrompt={onDeletePrompt}
@ -192,6 +207,6 @@ export const PromptFolder: FC<Props> = ({
} }
}) })
: null} : null}
</div> </>
); );
}; };

View File

@ -25,7 +25,7 @@ export const PromptFolders: FC<Props> = ({
onUpdatePrompt, onUpdatePrompt,
}) => { }) => {
return ( return (
<div className="flex w-full flex-col gap-1 pt-2"> <div className="flex w-full flex-col pt-2">
{folders.map((folder, index) => ( {folders.map((folder, index) => (
<PromptFolder <PromptFolder
key={index} key={index}