From 5e827dca98b2caf04b51d822045f321fc1d6b68f Mon Sep 17 00:00:00 2001 From: Albert Yu Date: Thu, 28 Nov 2024 17:25:38 +0800 Subject: [PATCH] Optionally delay entering/starting transition status --- .../experiments/collapsible-transitions.tsx | 77 +++++++++++++++++++ .../app/experiments/collapsible.module.css | 14 +++- .../collapsible/root/useCollapsibleRoot.ts | 2 +- .../react/src/utils/useTransitionStatus.ts | 13 +++- 4 files changed, 101 insertions(+), 5 deletions(-) create mode 100644 docs/src/app/experiments/collapsible-transitions.tsx diff --git a/docs/src/app/experiments/collapsible-transitions.tsx b/docs/src/app/experiments/collapsible-transitions.tsx new file mode 100644 index 0000000000..ed115f4422 --- /dev/null +++ b/docs/src/app/experiments/collapsible-transitions.tsx @@ -0,0 +1,77 @@ +'use client'; +import * as React from 'react'; +import { Collapsible } from '@base-ui-components/react/collapsible'; +import classes from './collapsible.module.css'; + +function classNames(...c: Array) { + return c.filter(Boolean).join(' '); +} + +export default function CollapsibleTransitions() { + return ( +
+
+ + + + Trigger 1A + + +

This is the collapsed content

+

+ You can find the Base UI repository{' '} + + here + +

+
+
+
+ +
+ + + + Trigger 1B + + +

This is the collapsed content

+

+ You can find the Base UI repository{' '} + + here + +

+
+
+
+
+ ); +} + +function ExpandMoreIcon(props: React.SVGProps) { + return ( + + + + ); +} diff --git a/docs/src/app/experiments/collapsible.module.css b/docs/src/app/experiments/collapsible.module.css index d4e2bfd0ec..38bb0a6c57 100644 --- a/docs/src/app/experiments/collapsible.module.css +++ b/docs/src/app/experiments/collapsible.module.css @@ -33,7 +33,7 @@ transition: transform var(--duration) ease-in; } -.trigger[data-open] .icon { +.trigger[data-panel-open] .icon { transform: rotate(0); transition: transform var(--duration) ease-out; } @@ -96,3 +96,15 @@ height: 0; } } + +.grid { + --width: 320px; + --duration: 2000ms; + + font-family: system-ui, sans-serif; + line-height: 1.4; + + display: grid; + grid: var(--width) var(--width) / var(--width) var(--width); + grid-gap: 4rem; +} diff --git a/packages/react/src/collapsible/root/useCollapsibleRoot.ts b/packages/react/src/collapsible/root/useCollapsibleRoot.ts index d40e21d105..ce7f7e026b 100644 --- a/packages/react/src/collapsible/root/useCollapsibleRoot.ts +++ b/packages/react/src/collapsible/root/useCollapsibleRoot.ts @@ -23,7 +23,7 @@ export function useCollapsibleRoot( state: 'open', }); - const { mounted, setMounted, transitionStatus } = useTransitionStatus(open, animated); + const { mounted, setMounted, transitionStatus } = useTransitionStatus(open, animated, true); const [panelId, setPanelId] = React.useState(useId()); diff --git a/packages/react/src/utils/useTransitionStatus.ts b/packages/react/src/utils/useTransitionStatus.ts index 258a584410..9a12f3a959 100644 --- a/packages/react/src/utils/useTransitionStatus.ts +++ b/packages/react/src/utils/useTransitionStatus.ts @@ -8,9 +8,12 @@ export type TransitionStatus = 'entering' | 'exiting' | undefined; * Provides a status string for CSS animations. * @param open - a boolean that determines if the element is open. * @param enabled - a boolean that determines if the logic is enabled. + * @param delayEnteringStatus - a boolean that set the `entering` status one + * tick later. Example use-case: collapsible needs an extra frame in order + * to measure the panel contents. * @ignore - internal hook. */ -export function useTransitionStatus(open: boolean, enabled = true) { +export function useTransitionStatus(open: boolean, enabled = true, delayEnteringStatus = false) { const [transitionStatus, setTransitionStatus] = React.useState(); const [mounted, setMounted] = React.useState(open); @@ -19,7 +22,7 @@ export function useTransitionStatus(open: boolean, enabled = true) { if (enabled) { if (open && !mounted) { setMounted(true); - if (transitionStatus !== 'entering') { + if (transitionStatus !== 'entering' && !delayEnteringStatus) { setTransitionStatus('entering'); } } @@ -38,6 +41,10 @@ export function useTransitionStatus(open: boolean, enabled = true) { return undefined; } + if (delayEnteringStatus) { + setTransitionStatus('entering'); + } + const frame = requestAnimationFrame(() => { setTransitionStatus(undefined); }); @@ -45,7 +52,7 @@ export function useTransitionStatus(open: boolean, enabled = true) { return () => { cancelAnimationFrame(frame); }; - }, [enabled, open]); + }, [enabled, open, delayEnteringStatus]); return React.useMemo( () => ({