💡 style: switched to Ariakit's tooltip (#3748)

* inital Tooltip implementation and test

* style(tooltip): L/R sidePanel and Nav

* style(tooltip): unarchive button; refactor: `useArchiveHandler` and `ArchiveButton`

* style(tooltip): Delete button

* refactor: remove unused className prop in DeleteButton component

* style(tooltip): finish final tooltip and fix bookmark edit and delete button

* refactor(ui): remove TooltipTest and DropDownMenu component and unused imports

* style: update mobile UI

* fix: sidePanel icon not showing

* feat(AttachFile): add tooltip

* fix(NavToggle): remove button
without this button, kb users don't have to manually press 2 times to change the focus
Also, tooltips with buttons focus don't trigger

* fix: right side panel issue with double button

* fix: merge issues

* fix: sharedLink table issue

* chore: update ariakit and framer-motion version

* a11y: kb toggle for sidebar

* feat: tooltip for some buttons
This commit is contained in:
Marco Beretta 2024-09-13 08:59:09 -04:00 committed by GitHub
parent e293ff63f9
commit 4ef5ae6f71
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
37 changed files with 747 additions and 967 deletions

View file

@ -1,87 +0,0 @@
import React from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import type { MouseEvent, FocusEvent, KeyboardEvent } from 'react';
import { TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from '~/components/ui';
import { useConversations, useLocalize, useNewConvo } from '~/hooks';
import { useArchiveConversationMutation } from '~/data-provider';
import { NotificationSeverity } from '~/common';
import { useToastContext } from '~/Providers';
type ArchiveButtonProps = {
children?: React.ReactNode;
conversationId: string;
retainView: () => void;
shouldArchive: boolean;
icon?: React.ReactNode;
className?: string;
};
export function useArchiveHandler(
conversationId: string,
shouldArchive: boolean,
retainView: () => void,
) {
const localize = useLocalize();
const navigate = useNavigate();
const { showToast } = useToastContext();
const { newConversation } = useNewConvo();
const { refreshConversations } = useConversations();
const { conversationId: currentConvoId } = useParams();
const archiveConvoMutation = useArchiveConversationMutation(conversationId);
return async (e?: MouseEvent | FocusEvent | KeyboardEvent) => {
if (e) {
e.preventDefault();
}
const label = shouldArchive ? 'archive' : 'unarchive';
archiveConvoMutation.mutate(
{ conversationId, isArchived: shouldArchive },
{
onSuccess: () => {
if (currentConvoId === conversationId || currentConvoId === 'new') {
newConversation();
navigate('/c/new', { replace: true });
}
refreshConversations();
retainView();
},
onError: () => {
showToast({
message: localize(`com_ui_${label}_error`),
severity: NotificationSeverity.ERROR,
showIcon: true,
});
},
},
);
};
}
export default function ArchiveButton({
conversationId,
retainView,
shouldArchive,
icon,
className = '',
}: ArchiveButtonProps) {
const localize = useLocalize();
const archiveHandler = useArchiveHandler(conversationId, shouldArchive, retainView);
return (
<button type="button" className={className} onClick={archiveHandler}>
<TooltipProvider delayDuration={250}>
<Tooltip>
<TooltipTrigger asChild>
<span className="h-5 w-5">{icon}</span>
</TooltipTrigger>
<TooltipContent side="top" sideOffset={0}>
{localize(`com_ui_${shouldArchive ? 'archive' : 'unarchive'}`)}
</TooltipContent>
</Tooltip>
</TooltipProvider>
</button>
);
}
export { useArchiveHandler as archiveHandler };

View file

@ -2,11 +2,10 @@ import { useState, useId } from 'react';
import * as Ariakit from '@ariakit/react';
import { Ellipsis, Share2, Archive, Pen, Trash } from 'lucide-react';
import { useGetStartupConfig } from 'librechat-data-provider/react-query';
import { useArchiveHandler } from './ArchiveButton';
import { useLocalize, useArchiveHandler } from '~/hooks';
import { DropdownPopup } from '~/components/ui';
import DeleteButton from './DeleteButton';
import ShareButton from './ShareButton';
import { useLocalize } from '~/hooks';
import { cn } from '~/utils';
export default function ConvoOptions({

View file

@ -4,15 +4,7 @@ import { useQueryClient } from '@tanstack/react-query';
import { useParams, useNavigate } from 'react-router-dom';
import type { TMessage } from 'librechat-data-provider';
import { useDeleteConversationMutation } from '~/data-provider';
import {
OGDialog,
OGDialogTrigger,
Label,
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from '~/components/ui';
import { OGDialog, OGDialogTrigger, Label, TooltipAnchor } from '~/components/ui';
import OGDialogTemplate from '~/components/ui/OGDialogTemplate';
import { TrashIcon } from '~/components/svg';
import { useLocalize, useNewConvo } from '~/hooks';
@ -21,7 +13,6 @@ type DeleteButtonProps = {
conversationId: string;
retainView: () => void;
title: string;
className?: string;
showDeleteDialog?: boolean;
setShowDeleteDialog?: (value: boolean) => void;
};
@ -30,7 +21,6 @@ export default function DeleteButton({
conversationId,
retainView,
title,
className = '',
showDeleteDialog,
setShowDeleteDialog,
}: DeleteButtonProps) {
@ -92,20 +82,13 @@ export default function DeleteButton({
return (
<OGDialog open={open} onOpenChange={setOpen}>
<TooltipProvider delayDuration={250}>
<Tooltip>
<OGDialogTrigger asChild>
<TooltipTrigger asChild>
<button>
<TrashIcon className="h-5 w-5" />
</button>
</TooltipTrigger>
</OGDialogTrigger>
<TooltipContent side="top" sideOffset={0} className={className}>
{localize('com_ui_delete')}
</TooltipContent>
</Tooltip>
</TooltipProvider>
<TooltipAnchor description={localize('com_ui_delete')}>
<OGDialogTrigger asChild>
<button>
<TrashIcon className="h-5 w-5" />
</button>
</OGDialogTrigger>
</TooltipAnchor>
{dialogContent}
</OGDialog>
);

View file

@ -1,4 +1,3 @@
export { default as ArchiveButton } from './ArchiveButton';
export { default as DeleteButton } from './DeleteButton';
export { default as ShareButton } from './ShareButton';
export { default as SharedLinkButton } from './SharedLinkButton';

View file

@ -1,71 +0,0 @@
import { cloneElement, type FC } from 'react';
import { DotsIcon } from '~/components/svg';
import { Content, Portal, Root, Trigger } from '@radix-ui/react-popover';
import { TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from '~/components/ui';
import { useToggle } from './ToggleContext';
import { useLocalize } from '~/hooks';
import { cn } from '~/utils';
type DropDownMenuProps = {
children: React.ReactNode;
icon?: React.ReactElement;
tooltip?: string;
className?: string;
};
const DropDownMenu: FC<DropDownMenuProps> = ({
children,
icon = <DotsIcon />,
tooltip = 'More',
className,
}: DropDownMenuProps) => {
const localize = useLocalize();
const { isPopoverActive, setPopoverActive } = useToggle();
return (
<Root open={isPopoverActive} onOpenChange={(open) => setPopoverActive(open)}>
<Trigger asChild>
<div
className={cn(
'pointer-cursor relative flex flex-col text-left focus:outline-none focus:ring-0 focus:ring-offset-0 sm:text-sm',
'hover:text-gray-400 radix-state-open:text-gray-400 dark:hover:text-gray-400 dark:radix-state-open:text-gray-400',
'z-50 flex h-[40px] min-w-4 flex-none items-center justify-center focus:ring-0 focus:ring-offset-0',
)}
id="edit-menu-button"
data-testid="edit-menu-button"
title={localize('com_ui_more_options')}
>
<TooltipProvider delayDuration={500}>
<Tooltip>
<TooltipTrigger asChild>
<button type="button" className={className}>
{cloneElement(icon, {
className:
'h-[18px] w-[18px] flex-shrink-0 text-gray-500 hover:text-gray-400 dark:text-gray-300 dark:hover:text-gray-400',
})}
</button>
</TooltipTrigger>
<TooltipContent side="top" sideOffset={0}>
{tooltip}
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
</Trigger>
<Portal>
<Content
side="bottom"
align="start"
className={cn(
'popover radix-side-bottom:animate-slideUpAndFade radix-side-left:animate-slideRightAndFade radix-side-right:animate-slideLeftAndFade radix-side-top:animate-slideDownAndFade overflow-hidden rounded-lg shadow-lg',
'border border-gray-200 bg-white dark:border-gray-600 dark:bg-gray-700 dark:text-white',
'flex min-w-[200px] max-w-xs flex-wrap',
)}
>
{children}
</Content>
</Portal>
</Root>
);
};
export default DropDownMenu;