mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-20 02:10:15 +01:00
💡 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:
parent
e293ff63f9
commit
4ef5ae6f71
37 changed files with 747 additions and 967 deletions
|
|
@ -1,46 +1,65 @@
|
|||
import * as React from 'react';
|
||||
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
||||
import { cn } from '~/utils';
|
||||
import * as Ariakit from '@ariakit/react';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { forwardRef, useMemo } from 'react';
|
||||
|
||||
const Tooltip = TooltipPrimitive.Root;
|
||||
interface TooltipAnchorProps extends Ariakit.TooltipAnchorProps {
|
||||
description: string;
|
||||
side?: 'top' | 'bottom' | 'left' | 'right';
|
||||
}
|
||||
|
||||
const TooltipTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof TooltipPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Trigger>
|
||||
>((props, ref) => <TooltipPrimitive.Trigger ref={ref} {...props} />);
|
||||
TooltipTrigger.displayName = TooltipPrimitive.Trigger.displayName;
|
||||
export const TooltipAnchor = forwardRef<HTMLDivElement, TooltipAnchorProps>(function TooltipAnchor(
|
||||
{ description, side = 'top', role, ...props },
|
||||
ref,
|
||||
) {
|
||||
const tooltip = Ariakit.useTooltipStore({ placement: side });
|
||||
const mounted = Ariakit.useStoreState(tooltip, (state) => state.mounted);
|
||||
const placement = Ariakit.useStoreState(tooltip, (state) => state.placement);
|
||||
|
||||
const TooltipPortal = TooltipPrimitive.Portal;
|
||||
const { x, y } = useMemo(() => {
|
||||
const dir = placement.split('-')[0];
|
||||
switch (dir) {
|
||||
case 'top':
|
||||
return { x: 0, y: -8 };
|
||||
case 'bottom':
|
||||
return { x: 0, y: 8 };
|
||||
case 'left':
|
||||
return { x: -8, y: 0 };
|
||||
case 'right':
|
||||
return { x: 8, y: 0 };
|
||||
default:
|
||||
return { x: 0, y: 0 };
|
||||
}
|
||||
}, [placement]);
|
||||
|
||||
const TooltipArrow = React.forwardRef<
|
||||
React.ElementRef<typeof TooltipPrimitive.Arrow>,
|
||||
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Arrow>
|
||||
>((props, ref) => <TooltipPrimitive.Arrow ref={ref} {...props} />);
|
||||
TooltipArrow.displayName = TooltipPrimitive.Arrow.displayName;
|
||||
const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
if (role === 'button' && event.key === 'Enter') {
|
||||
event.preventDefault();
|
||||
(event.target as HTMLDivElement).click();
|
||||
}
|
||||
};
|
||||
|
||||
const TooltipContent = React.forwardRef<
|
||||
React.ElementRef<typeof TooltipPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
||||
>(({ className = '', forceMount, children, ...props }, ref) => (
|
||||
<TooltipPortal forceMount={forceMount}>
|
||||
<TooltipPrimitive.Content
|
||||
className={cn(
|
||||
'shadow-xs relative z-[1000] max-w-xs rounded-lg border border-gray-900/10 bg-gray-900 p-1 transition-opacity',
|
||||
className,
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
style={{ userSelect: 'none' }}
|
||||
>
|
||||
<span className="flex items-center whitespace-pre-wrap px-2 py-1 text-center text-sm font-medium normal-case text-white">
|
||||
{children}
|
||||
<TooltipArrow className="TooltipArrow" />
|
||||
</span>
|
||||
</TooltipPrimitive.Content>
|
||||
</TooltipPortal>
|
||||
));
|
||||
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
||||
|
||||
const TooltipProvider = TooltipPrimitive.Provider;
|
||||
|
||||
export { Tooltip, TooltipTrigger, TooltipPortal, TooltipContent, TooltipArrow, TooltipProvider };
|
||||
return (
|
||||
<Ariakit.TooltipProvider store={tooltip} hideTimeout={0}>
|
||||
<Ariakit.TooltipAnchor {...props} ref={ref} role={role} onKeyDown={handleKeyDown} />
|
||||
<AnimatePresence>
|
||||
{mounted && (
|
||||
<Ariakit.Tooltip
|
||||
gutter={4}
|
||||
alwaysVisible
|
||||
className="tooltip"
|
||||
render={
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x, y }}
|
||||
animate={{ opacity: 1, x: 0, y: 0 }}
|
||||
exit={{ opacity: 0, x, y }}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Ariakit.TooltipArrow />
|
||||
{description}
|
||||
</Ariakit.Tooltip>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</Ariakit.TooltipProvider>
|
||||
);
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue