Skip to content

Commit

Permalink
Support CSS animations/transitions correctly
Browse files Browse the repository at this point in the history
  • Loading branch information
atomiks committed Apr 17, 2024
1 parent aad6e79 commit b3f0b25
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 33 deletions.
78 changes: 60 additions & 18 deletions docs/pages/experiments/tooltip.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
import * as React from 'react';
import * as Tooltip from '@base_ui/react/Tooltip';
import { styled } from '@mui/system';
import { styled, keyframes, css } from '@mui/system';

const scaleIn = keyframes`
from {
opacity: 0;
transform: scale(0.9);
}
`;

const scaleOut = keyframes`
to {
opacity: 0;
transform: scale(0.9);
}
`;

const blue = {
400: '#3399FF',
Expand All @@ -9,25 +23,34 @@ const blue = {
};

export const TooltipContent = styled(Tooltip.Content)`
${({ theme }) => `
${({ theme }) => css`
font-family: 'IBM Plex Sans', sans-serif;
background: ${theme.palette.mode === 'dark' ? 'white' : 'black'};
color: ${theme.palette.mode === 'dark' ? 'black' : 'white'};
padding: 4px 6px;
border-radius: 4px;
font-size: 95%;
cursor: default;
transition-property: opacity, transform;
transform-origin: var(--transform-origin);
&[data-status='open'],
&[data-status='closed'] {
transition-duration: 0.2s;
}
&[data-status='initial'],
&[data-status='closed'] {
&[data-transition] {
transition-property: opacity, transform;
transition-duration: 0.15s;
opacity: 0;
transform: scale(0.9);
&[data-status='opening'] {
opacity: 1;
transform: scale(1);
}
}
&[data-animation] {
animation: ${scaleIn} 0.2s;
&[data-status='closing'] {
animation: ${scaleOut} 0.2s forwards;
}
}
`}
`;
Expand All @@ -53,15 +76,34 @@ export const AnchorButton = styled('button')`

export default function TooltipTransitionExperiment() {
return (
<div style={{ width: 700, margin: '0 auto', padding: 50 }}>
<Tooltip.Root>
<Tooltip.AnchorFragment>
<AnchorButton>Anchor</AnchorButton>
</Tooltip.AnchorFragment>
<TooltipContent sideOffset={7} arrowPadding={3}>
Tooltip
</TooltipContent>
</Tooltip.Root>
<div
style={{
width: 700,
padding: 50,
margin: '0 auto',
fontFamily: '"IBM Plex Sans", sans-serif',
}}
>
<h2>Transition</h2>
<Tooltip.Group>
<Tooltip.Root>
<Tooltip.AnchorFragment>
<AnchorButton>Anchor</AnchorButton>
</Tooltip.AnchorFragment>
<TooltipContent data-transition sideOffset={7}>
Tooltip
</TooltipContent>
</Tooltip.Root>
<h2>Animation</h2>
<Tooltip.Root>
<Tooltip.AnchorFragment>
<AnchorButton>Anchor</AnchorButton>
</Tooltip.AnchorFragment>
<TooltipContent data-animation sideOffset={7}>
Tooltip
</TooltipContent>
</Tooltip.Root>
</Tooltip.Group>
</div>
);
}
2 changes: 1 addition & 1 deletion packages/mui-base/src/Tooltip/Tooltip.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { FloatingArrowProps, FloatingContext, OpenChangeReason } from '@flo
import type { BaseUIComponentProps, GenericHTMLProps } from '../utils/BaseUI.types';
import type { TooltipContentParameters } from '../useTooltip';

export type Status = 'unmounted' | 'initial' | 'open' | 'closed';
export type Status = 'unmounted' | 'initial' | 'opening' | 'closing';

export interface ContextValue {
open: boolean;
Expand Down
1 change: 0 additions & 1 deletion packages/mui-base/src/Tooltip/TooltipArrow.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ describe('<Tooltip.Arrow />', () => {
const { render } = createRenderer();

describeConformance(<Tooltip.Arrow />, () => ({
inheritComponent: 'svg',
refInstanceof: window.Element,
render(node) {
return render(
Expand Down
4 changes: 2 additions & 2 deletions packages/mui-base/src/Tooltip/useTransitionStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ export function useTransitionStatus(trigger: boolean) {
setStatus('initial');

const frame = requestAnimationFrame(() => {
setStatus('open');
setStatus('opening');
});

return () => {
cancelAnimationFrame(frame);
};
}

setStatus('closed');
setStatus('closing');

return undefined;
}, [trigger]);
Expand Down
33 changes: 22 additions & 11 deletions packages/mui-base/src/useTooltip/useTooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,14 @@ export function useTooltip(params: UseTooltipParameters): UseTooltipReturnValue
open,
onOpenChange(openValue, eventValue, reasonValue) {
onOpenChange(openValue, eventValue, reasonValue);
const transitioningChild = refs.floating.current?.firstElementChild;
if (!openValue && transitioningChild) {
const computedStyles = ownerWindow(transitioningChild).getComputedStyle(transitioningChild);
const transitionDuration = parseFloat(computedStyles.transitionDuration);
if (transitionDuration === 0) {

const contentElement = refs.floating.current?.firstElementChild;
if (!openValue && contentElement) {
const computedStyles = ownerWindow(contentElement).getComputedStyle(contentElement);
const noTransitionDuration = ['', '0s'].includes(computedStyles.transitionDuration);
const noAnimationName = ['', 'none'].includes(computedStyles.animationName);
const noAnimation = noTransitionDuration && noAnimationName;
if (noAnimation && !keepMounted) {
setMounted(false);
}
}
Expand Down Expand Up @@ -215,8 +218,15 @@ export function useTooltip(params: UseTooltipParameters): UseTooltipReturnValue
);

const getContentProps: UseTooltipReturnValue['getContentProps'] = React.useCallback(
(externalProps = {}) =>
mergeReactProps(
(externalProps = {}) => {
function handleTransitionOrAnimationEnd({ target }: React.SyntheticEvent) {
const contentElement = refs.floating.current?.firstElementChild;
if (target === contentElement) {
setMounted((prevMounted) => (prevMounted ? false : prevMounted));
}
}

return mergeReactProps(
externalProps,
getFloatingProps({
['data-side' as string]: renderedSide,
Expand All @@ -230,11 +240,11 @@ export function useTooltip(params: UseTooltipParameters): UseTooltipReturnValue
pointerEvents: isHidden || followCursorAxis === 'both' ? 'none' : undefined,
zIndex: 2147483647, // max z-index
},
onTransitionEnd() {
setMounted((prevMounted) => (prevMounted ? false : prevMounted));
},
onTransitionEnd: handleTransitionOrAnimationEnd,
onAnimationEnd: handleTransitionOrAnimationEnd,
}),
),
);
},
[
getFloatingProps,
floatingStyles,
Expand All @@ -244,6 +254,7 @@ export function useTooltip(params: UseTooltipParameters): UseTooltipReturnValue
renderedAlignment,
status,
setMounted,
refs,
],
);

Expand Down

0 comments on commit b3f0b25

Please sign in to comment.