mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
feat: add transition status to React tooltip (#42)
This commit is contained in:
@@ -16,6 +16,7 @@ import {
|
||||
useHover,
|
||||
useInteractions,
|
||||
useRole,
|
||||
useTransitionStatus,
|
||||
} from '@floating-ui/react';
|
||||
|
||||
interface UpdatePositioningProps {
|
||||
@@ -25,15 +26,12 @@ interface UpdatePositioningProps {
|
||||
}
|
||||
|
||||
interface TooltipContextType {
|
||||
open: boolean;
|
||||
setOpen: (open: boolean) => void;
|
||||
refs: ReturnType<typeof useFloating>['refs'];
|
||||
floatingStyles: ReturnType<typeof useFloating>['floatingStyles'];
|
||||
arrowRef: React.MutableRefObject<HTMLElement | null>;
|
||||
context: ReturnType<typeof useFloating>['context'];
|
||||
transitionStatus: ReturnType<typeof useTransitionStatus>['status'];
|
||||
getReferenceProps: ReturnType<typeof useInteractions>['getReferenceProps'];
|
||||
getFloatingProps: ReturnType<typeof useInteractions>['getFloatingProps'];
|
||||
context: ReturnType<typeof useFloating>['context'];
|
||||
updatePositioning: (props: UpdatePositioningProps) => void;
|
||||
arrowRef: React.MutableRefObject<HTMLElement | null>;
|
||||
}
|
||||
|
||||
interface TooltipRootProps {
|
||||
@@ -100,7 +98,7 @@ function TooltipRoot({ delay = 600, closeDelay = 0, children }: TooltipRootProps
|
||||
const [collisionPadding, setCollisionPadding] = useState(0);
|
||||
const arrowRef = useRef<HTMLElement | null>(null);
|
||||
|
||||
const { refs, floatingStyles, context } = useFloating({
|
||||
const { context } = useFloating({
|
||||
open,
|
||||
onOpenChange: setOpen,
|
||||
placement,
|
||||
@@ -115,6 +113,8 @@ function TooltipRoot({ delay = 600, closeDelay = 0, children }: TooltipRootProps
|
||||
whileElementsMounted: autoUpdate,
|
||||
});
|
||||
|
||||
const { status: transitionStatus } = useTransitionStatus(context);
|
||||
|
||||
const hover = useHover(context, {
|
||||
delay: {
|
||||
open: delay,
|
||||
@@ -134,41 +134,37 @@ function TooltipRoot({ delay = 600, closeDelay = 0, children }: TooltipRootProps
|
||||
};
|
||||
|
||||
const value: TooltipContextType = {
|
||||
open,
|
||||
setOpen,
|
||||
refs,
|
||||
floatingStyles,
|
||||
getReferenceProps,
|
||||
getFloatingProps,
|
||||
context,
|
||||
updatePositioning,
|
||||
arrowRef,
|
||||
transitionStatus,
|
||||
};
|
||||
|
||||
return <TooltipContext.Provider value={value}>{children}</TooltipContext.Provider>;
|
||||
}
|
||||
|
||||
function TooltipTrigger({ children }: TooltipTriggerProps): JSX.Element {
|
||||
const { refs, getReferenceProps } = useTooltipContext();
|
||||
const { context, getReferenceProps } = useTooltipContext();
|
||||
const { refs, open } = context;
|
||||
|
||||
return React.cloneElement(React.Children.only(children) as JSX.Element, {
|
||||
ref: refs.setReference,
|
||||
...getReferenceProps(),
|
||||
'data-popup-open': open ? '' : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
function TooltipPositioner({ side = 'top', sideOffset = 0, collisionPadding = 0, children }: TooltipPositionerProps): JSX.Element | null {
|
||||
const { open, refs, floatingStyles, updatePositioning } = useTooltipContext();
|
||||
const { context, updatePositioning } = useTooltipContext();
|
||||
const { refs, floatingStyles } = context;
|
||||
|
||||
// Update positioning when props change
|
||||
useEffect(() => {
|
||||
updatePositioning({ side, sideOffset, collisionPadding });
|
||||
}, [side, sideOffset, collisionPadding, updatePositioning]);
|
||||
|
||||
if (!open) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const positionerContextValue: TooltipPositionerContextType = {
|
||||
side,
|
||||
};
|
||||
@@ -182,9 +178,9 @@ function TooltipPositioner({ side = 'top', sideOffset = 0, collisionPadding = 0,
|
||||
);
|
||||
}
|
||||
|
||||
function TooltipPopup({ className = '', children }: TooltipPopupProps): JSX.Element {
|
||||
const { getFloatingProps } = useTooltipContext();
|
||||
const { refs } = useTooltipContext();
|
||||
function TooltipPopup({ className = '', children }: TooltipPopupProps): JSX.Element | null {
|
||||
const { context, getFloatingProps, transitionStatus } = useTooltipContext();
|
||||
const { refs, placement } = context;
|
||||
const triggerElement = refs.reference.current as HTMLElement | null;
|
||||
|
||||
// Copy data attributes from trigger element
|
||||
@@ -197,7 +193,16 @@ function TooltipPopup({ className = '', children }: TooltipPopupProps): JSX.Elem
|
||||
: {};
|
||||
|
||||
return (
|
||||
<div className={className} {...getFloatingProps()} {...dataAttributes}>
|
||||
<div
|
||||
className={className}
|
||||
{...getFloatingProps()}
|
||||
{...dataAttributes}
|
||||
data-side={placement}
|
||||
data-starting-style={transitionStatus === 'initial' ? '' : undefined}
|
||||
data-open={transitionStatus === 'initial' || transitionStatus === 'open' ? '' : undefined}
|
||||
data-ending-style={transitionStatus === 'close' || transitionStatus === 'unmounted' ? '' : undefined}
|
||||
data-closed={transitionStatus === 'close' || transitionStatus === 'unmounted' ? '' : undefined}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -70,18 +70,17 @@ const styles: MediaDefaultSkinStyles = {
|
||||
PlayIcon: cn('play-icon'),
|
||||
PauseIcon: cn('pause-icon'),
|
||||
TooltipPopup: cn(
|
||||
'whitespace-nowrap relative px-2 py-1 text-xs font-medium rounded-md text-white text-shadow',
|
||||
'whitespace-nowrap flex origin-[var(--transform-origin)] flex-col rounded-md text-white text-xs @7xl/root:text-sm px-2 py-1',
|
||||
// Background
|
||||
'bg-white/10 backdrop-blur-3xl backdrop-saturate-150 backdrop-brightness-90',
|
||||
// Shadow
|
||||
// 'shadow-lg shadow-black/25',
|
||||
// Border
|
||||
'border border-white/10',
|
||||
// Reduced transparency for users with preference
|
||||
// XXX: This requires a Tailwind custom variant (see 1 below)
|
||||
'reduced-transparency:bg-black/70 reduced-transparency:ring-black reduced-transparency:after:ring-white/20',
|
||||
// High contrast mode
|
||||
'contrast-more:bg-black/90 contrast-more:ring-black contrast-more:after:ring-white/20'
|
||||
// Animation
|
||||
'transition-[transform,scale,opacity] data-[ending-style]:scale-90 data-[ending-style]:opacity-0 data-[instant]:duration-0 data-[starting-style]:scale-90 data-[starting-style]:opacity-0',
|
||||
// Ring
|
||||
'ring-1 ring-white/10 ring-inset',
|
||||
// Text shadow
|
||||
'text-shadow shadow-black/10',
|
||||
// Border to enhance contrast on lighter videos
|
||||
'after:absolute after:inset-0 after:ring after:rounded-[inherit] after:ring-black/15 after:pointer-events-none',
|
||||
),
|
||||
PlayTooltipPopup: cn(
|
||||
'[&_.pause-tooltip]:inline [&[data-paused]_.pause-tooltip]:hidden',
|
||||
|
||||
Reference in New Issue
Block a user