From 96581d56df826f7f58a3b642d8e1b3ac1f3a03bf Mon Sep 17 00:00:00 2001 From: Marco Beretta <81851188+berry-13@users.noreply.github.com> Date: Fri, 16 Aug 2024 10:30:14 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=96=BC=EF=B8=8F=20style:=20Conversation?= =?UTF-8?q?=20Menu=20and=20Dialogs=20update=20(#3601)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: new dropdown * fix: maintain popover active when open * fix: update DeleteButton and ShareButton component to use useState for managing dialog state * BREAKING: style improvement of base Button component * style: update export button * a11y: ExportAndShareButton * add border * quick style fix * fix: flick issue on convo * fix: DropDown opens when renaming * chore: update radix-ui/react-dropdown-menu to latest * small fix * style: bookmarks update * reorder export modal * feat: imporved dropdowns * style: a lot of changes; header, bookmarks, export, nav, convo, convoOptions * fix: small style issues * fix: button * fix: bookmarks header menu * fix: dropdown close glitch * feat: Improve accessibility and keyboard navigation in ModelSpec component * fix: Nav related type issues * style: ConvoOptions theming and focus ring --------- Co-authored-by: Danny Avila --- api/models/ConversationTag.js | 12 +- client/package.json | 2 +- .../Bookmarks/BookmarkEditDialog.tsx | 14 +- .../src/components/Bookmarks/BookmarkForm.tsx | 2 +- .../src/components/Bookmarks/BookmarkItem.tsx | 49 +- .../components/Bookmarks/BookmarkItems.tsx | 17 +- .../Bookmarks/DeleteBookmarkButton.tsx | 2 +- .../Bookmarks/EditBookmarkButton.tsx | 48 +- client/src/components/Chat/AddMultiConvo.tsx | 12 +- .../components/Chat/ExportAndShareMenu.tsx | 107 +- client/src/components/Chat/ExportButton.tsx | 44 - client/src/components/Chat/Footer.tsx | 35 +- client/src/components/Chat/Header.tsx | 7 +- .../components/Chat/Input/OptionsPopover.tsx | 6 +- .../components/Chat/Menus/BookmarkMenu.tsx | 162 +- .../Menus/Bookmarks/BookmarkMenuItems.tsx | 82 +- .../Chat/Menus/Models/ModelSpec.tsx | 19 +- .../Chat/Menus/Presets/PresetItems.tsx | 8 +- client/src/components/Conversations/Convo.tsx | 160 +- .../{ => ConvoOptions}/ArchiveButton.tsx | 45 +- .../ConvoOptions/ConvoOptions.tsx | 101 + .../{ => ConvoOptions}/DeleteButton.tsx | 103 +- .../ConvoOptions/ShareButton.tsx | 100 + .../{ => ConvoOptions}/SharedLinkButton.tsx | 0 .../Conversations/ConvoOptions/index.ts | 5 + .../components/Conversations/RenameButton.tsx | 46 - .../components/Conversations/ShareButton.tsx | 113 - .../components/Conversations/ShareDialog.tsx | 80 - client/src/components/Conversations/index.ts | 2 +- .../components/Nav/Bookmarks/BookmarkNav.tsx | 63 +- .../Nav/Bookmarks/BookmarkNavItems.tsx | 10 +- .../Nav/ExportConversation/ExportModal.tsx | 17 +- client/src/components/Nav/Nav.tsx | 49 +- client/src/components/Nav/NavLinks.tsx | 4 +- client/src/components/Nav/NewChat.tsx | 6 +- client/src/components/Nav/SearchBar.tsx | 11 +- client/src/components/Nav/Settings.tsx | 14 +- .../SettingsTabs/General/ArchivedChats.tsx | 14 +- .../General/ArchivedChatsTable.tsx | 9 +- .../Prompts/Groups/FilterPrompts.tsx | 2 +- .../Prompts/Groups/PanelNavigation.tsx | 9 +- .../SidePanel/Bookmarks/BookmarkPanel.tsx | 15 +- .../SidePanel/Bookmarks/BookmarkTable.tsx | 4 +- .../components/SidePanel/Files/PanelTable.tsx | 2 +- client/src/components/SidePanel/Nav.tsx | 2 +- client/src/components/ui/Button.tsx | 96 +- client/src/components/ui/DropdownPopup.tsx | 100 + client/src/components/ui/Slider.tsx | 6 +- client/src/components/ui/index.ts | 1 + client/src/data-provider/mutations.ts | 6 +- client/src/hooks/Chat/useChatFunctions.ts | 2 +- client/src/hooks/Conversations/useSearch.ts | 10 +- client/src/hooks/Messages/useSubmitMessage.ts | 4 +- client/src/hooks/Nav/useNavScrolling.ts | 12 +- client/src/localization/languages/Eng.ts | 2 + client/src/routes/Root.tsx | 2 +- client/src/store/settings.ts | 2 +- client/src/style.css | 1908 +++++++++-------- client/src/utils/messages.ts | 2 +- client/tailwind.config.cjs | 2 +- package-lock.json | 689 +++++- packages/data-provider/src/types/queries.ts | 2 +- 62 files changed, 2633 insertions(+), 1827 deletions(-) delete mode 100644 client/src/components/Chat/ExportButton.tsx rename client/src/components/Conversations/{ => ConvoOptions}/ArchiveButton.tsx (76%) create mode 100644 client/src/components/Conversations/ConvoOptions/ConvoOptions.tsx rename client/src/components/Conversations/{ => ConvoOptions}/DeleteButton.tsx (50%) create mode 100644 client/src/components/Conversations/ConvoOptions/ShareButton.tsx rename client/src/components/Conversations/{ => ConvoOptions}/SharedLinkButton.tsx (100%) create mode 100644 client/src/components/Conversations/ConvoOptions/index.ts delete mode 100644 client/src/components/Conversations/RenameButton.tsx delete mode 100644 client/src/components/Conversations/ShareButton.tsx delete mode 100644 client/src/components/Conversations/ShareDialog.tsx create mode 100644 client/src/components/ui/DropdownPopup.tsx diff --git a/api/models/ConversationTag.js b/api/models/ConversationTag.js index d64dcfa5213..53d144e1f50 100644 --- a/api/models/ConversationTag.js +++ b/api/models/ConversationTag.js @@ -136,13 +136,13 @@ const adjustPositions = async (user, oldPosition, newPosition) => { const position = oldPosition < newPosition ? { - $gt: Math.min(oldPosition, newPosition), - $lte: Math.max(oldPosition, newPosition), - } + $gt: Math.min(oldPosition, newPosition), + $lte: Math.max(oldPosition, newPosition), + } : { - $gte: Math.min(oldPosition, newPosition), - $lt: Math.max(oldPosition, newPosition), - }; + $gte: Math.min(oldPosition, newPosition), + $lt: Math.max(oldPosition, newPosition), + }; await ConversationTag.updateMany( { diff --git a/client/package.json b/client/package.json index 2693ada9f78..6046baf8fb5 100644 --- a/client/package.json +++ b/client/package.json @@ -37,7 +37,7 @@ "@radix-ui/react-checkbox": "^1.0.3", "@radix-ui/react-collapsible": "^1.0.3", "@radix-ui/react-dialog": "^1.0.2", - "@radix-ui/react-dropdown-menu": "^2.0.2", + "@radix-ui/react-dropdown-menu": "^2.1.1", "@radix-ui/react-hover-card": "^1.0.5", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.0.0", diff --git a/client/src/components/Bookmarks/BookmarkEditDialog.tsx b/client/src/components/Bookmarks/BookmarkEditDialog.tsx index a57b61088fc..3b85edce42c 100644 --- a/client/src/components/Bookmarks/BookmarkEditDialog.tsx +++ b/client/src/components/Bookmarks/BookmarkEditDialog.tsx @@ -1,7 +1,7 @@ -import React, { useRef, useState } from 'react'; +import React, { useRef, useState, Dispatch, SetStateAction } from 'react'; import { TConversationTag, TConversation } from 'librechat-data-provider'; import OGDialogTemplate from '~/components/ui/OGDialogTemplate'; -import { OGDialog, OGDialogTrigger, OGDialogClose } from '~/components/ui/'; +import { OGDialog, OGDialogClose } from '~/components/ui/'; import BookmarkForm from './BookmarkForm'; import { useLocalize } from '~/hooks'; import { Spinner } from '../svg'; @@ -11,18 +11,20 @@ type BookmarkEditDialogProps = { conversation?: TConversation; tags?: string[]; setTags?: (tags: string[]) => void; - trigger: React.ReactNode; + open: boolean; + setOpen: Dispatch>; }; + const BookmarkEditDialog = ({ bookmark, conversation, tags, setTags, - trigger, + open, + setOpen, }: BookmarkEditDialogProps) => { const localize = useLocalize(); const [isLoading, setIsLoading] = useState(false); - const [open, setOpen] = useState(false); const formRef = useRef(null); const handleSubmitForm = () => { @@ -33,10 +35,8 @@ const BookmarkEditDialog = ({ return ( - {trigger} )} /> diff --git a/client/src/components/Bookmarks/BookmarkItem.tsx b/client/src/components/Bookmarks/BookmarkItem.tsx index 62f8f79a73a..07d66ddbb92 100644 --- a/client/src/components/Bookmarks/BookmarkItem.tsx +++ b/client/src/components/Bookmarks/BookmarkItem.tsx @@ -1,4 +1,5 @@ import { useState } from 'react'; +import { MenuItem } from '@headlessui/react'; import { BookmarkFilledIcon, BookmarkIcon } from '@radix-ui/react-icons'; import type { FC } from 'react'; import { Spinner } from '~/components/svg'; @@ -7,25 +8,19 @@ import { cn } from '~/utils'; type MenuItemProps = { tag: string | React.ReactNode; selected: boolean; - ctx: 'header' | 'nav'; count?: number; - handleSubmit: (tag: string) => Promise; + handleSubmit: (tag?: string) => Promise; icon?: React.ReactNode; - highlightSelected?: boolean; }; -const BookmarkItem: FC = ({ - tag, - ctx, - selected, - count, - handleSubmit, - icon, - highlightSelected, - ...rest -}) => { +const BookmarkItem: FC = ({ tag, selected, handleSubmit, icon, ...rest }) => { const [isLoading, setIsLoading] = useState(false); const clickHandler = async () => { + if (tag === 'New Bookmark') { + await handleSubmit(); + return; + } + setIsLoading(true); await handleSubmit(tag as string); setIsLoading(false); @@ -49,20 +44,15 @@ const BookmarkItem: FC = ({ return ; }; - const ariaLabel = - ctx === 'header' ? `${selected ? 'Remove' : 'Add'} bookmark for ${tag}` : (tag as string); - return ( - + ); }; diff --git a/client/src/components/Bookmarks/BookmarkItems.tsx b/client/src/components/Bookmarks/BookmarkItems.tsx index 9b4da3b5655..845bf40925b 100644 --- a/client/src/components/Bookmarks/BookmarkItems.tsx +++ b/client/src/components/Bookmarks/BookmarkItems.tsx @@ -2,35 +2,24 @@ import type { FC } from 'react'; import { useBookmarkContext } from '~/Providers/BookmarkContext'; import BookmarkItem from './BookmarkItem'; interface BookmarkItemsProps { - ctx: 'header' | 'nav'; tags: string[]; - handleSubmit: (tag: string) => Promise; + handleSubmit: (tag?: string) => Promise; header: React.ReactNode; - highlightSelected?: boolean; } -const BookmarkItems: FC = ({ - ctx, - tags, - handleSubmit, - header, - highlightSelected, -}) => { +const BookmarkItems: FC = ({ tags, handleSubmit, header }) => { const { bookmarks } = useBookmarkContext(); return ( <> {header} -
+ {bookmarks.length > 0 &&
} {bookmarks.map((bookmark) => ( ))} diff --git a/client/src/components/Bookmarks/DeleteBookmarkButton.tsx b/client/src/components/Bookmarks/DeleteBookmarkButton.tsx index a3a2d0b4746..e401f88843b 100644 --- a/client/src/components/Bookmarks/DeleteBookmarkButton.tsx +++ b/client/src/components/Bookmarks/DeleteBookmarkButton.tsx @@ -46,7 +46,7 @@ const DeleteBookmarkButton: FC<{ } confirm={confirmDelete} - className="transition-color flex h-7 w-7 min-w-7 items-center justify-center rounded-lg duration-200 hover:bg-gray-200 dark:hover:bg-gray-700" + className="transition-color flex size-7 items-center justify-center rounded-lg duration-200 hover:bg-surface-hover" icon={} tabIndex={tabIndex} onFocus={onFocus} diff --git a/client/src/components/Bookmarks/EditBookmarkButton.tsx b/client/src/components/Bookmarks/EditBookmarkButton.tsx index 58f18d6677b..63ab6948704 100644 --- a/client/src/components/Bookmarks/EditBookmarkButton.tsx +++ b/client/src/components/Bookmarks/EditBookmarkButton.tsx @@ -1,3 +1,4 @@ +import { useState } from 'react'; import type { FC } from 'react'; import type { TConversationTag } from 'librechat-data-provider'; import BookmarkEditDialog from './BookmarkEditDialog'; @@ -12,30 +13,31 @@ const EditBookmarkButton: FC<{ onBlur?: () => void; }> = ({ bookmark, tabIndex = 0, onFocus, onBlur }) => { const localize = useLocalize(); + const [open, setOpen] = useState(false); + return ( - - - - - - - - {localize('com_ui_edit')} - - - - - } - /> + <> + + + ); }; diff --git a/client/src/components/Chat/AddMultiConvo.tsx b/client/src/components/Chat/AddMultiConvo.tsx index 9daaded61d2..34b11e4074d 100644 --- a/client/src/components/Chat/AddMultiConvo.tsx +++ b/client/src/components/Chat/AddMultiConvo.tsx @@ -3,6 +3,7 @@ import { isAssistantsEndpoint } from 'librechat-data-provider'; import type { TConversation } from 'librechat-data-provider'; import { useChatContext, useAddedChatContext } from '~/Providers'; import { mainTextareaId } from '~/common'; +import { Button } from '~/components/ui'; import { cn } from '~/utils'; function AddMultiConvo({ className = '' }: { className?: string }) { @@ -32,16 +33,15 @@ function AddMultiConvo({ className = '' }: { className?: string }) { } return ( - + ); } diff --git a/client/src/components/Chat/ExportAndShareMenu.tsx b/client/src/components/Chat/ExportAndShareMenu.tsx index ba556499ee4..59c1abff2e5 100644 --- a/client/src/components/Chat/ExportAndShareMenu.tsx +++ b/client/src/components/Chat/ExportAndShareMenu.tsx @@ -1,64 +1,95 @@ import { useState } from 'react'; -import { Upload } from 'lucide-react'; +import { Upload, Share2 } from 'lucide-react'; import { useRecoilValue } from 'recoil'; -import DropDownMenu from '~/components/Conversations/DropDownMenu'; -import ShareButton from '~/components/Conversations/ShareButton'; -import HoverToggle from '~/components/Conversations/HoverToggle'; +import { ShareButton } from '~/components/Conversations/ConvoOptions'; +import { Button, DropdownPopup } from '~/components/ui'; import useLocalize from '~/hooks/useLocalize'; -import ExportButton from './ExportButton'; +import { ExportModal } from '../Nav'; import store from '~/store'; export default function ExportAndShareMenu({ isSharedButtonEnabled, - className = '', }: { isSharedButtonEnabled: boolean; - className?: string; }) { const localize = useLocalize(); - const conversation = useRecoilValue(store.conversationByIndex(0)); const [isPopoverActive, setIsPopoverActive] = useState(false); + const [showExports, setShowExports] = useState(false); + const [showShareDialog, setShowShareDialog] = useState(false); const exportable = conversation && - conversation.conversationId && + conversation.conversationId != null && conversation.conversationId !== 'new' && conversation.conversationId !== 'search'; - if (!exportable) { + if (exportable === false) { return null; } - const isActiveConvo = exportable; + const onOpenChange = (value: boolean) => { + setShowExports(value); + }; + + const shareHandler = () => { + setIsPopoverActive(false); + setShowShareDialog(true); + }; + + const exportHandler = () => { + setIsPopoverActive(false); + setShowExports(true); + }; + + const dropdownItems = [ + { + label: localize('com_endpoint_export'), + onClick: exportHandler, + icon: , + }, + { + label: localize('com_ui_share'), + onClick: shareHandler, + icon: , + show: isSharedButtonEnabled, + }, + ]; return ( - - } - tooltip={localize('com_endpoint_export_share')} - className="pointer-cursor relative z-50 flex h-[40px] min-w-4 flex-none flex-col items-center justify-center rounded-md border border-gray-100 bg-white px-3 text-left hover:bg-gray-50 focus:outline-none focus:ring-0 focus:ring-offset-0 radix-state-open:bg-gray-50 dark:border-gray-700 dark:bg-gray-800 dark:hover:bg-gray-700 dark:radix-state-open:bg-gray-700 sm:text-sm" - > - {conversation && conversation.conversationId && ( - <> - - {isSharedButtonEnabled && ( - - )} - - )} - - + <> + +