Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove transition state from render props #3312

Merged
merged 4 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/@headlessui-react/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Add ability to render multiple `Dialog` components at once (without nesting them) ([#3242](https://github.com/tailwindlabs/headlessui/pull/3242))
- Add new data-attribute-based transition API ([#3273](https://github.com/tailwindlabs/headlessui/pull/3273), [#3285](https://github.com/tailwindlabs/headlessui/pull/3285), [#3307](https://github.com/tailwindlabs/headlessui/pull/3307), [#3309](https://github.com/tailwindlabs/headlessui/pull/3309))
- Add new data-attribute-based transition API ([#3273](https://github.com/tailwindlabs/headlessui/pull/3273), [#3285](https://github.com/tailwindlabs/headlessui/pull/3285), [#3307](https://github.com/tailwindlabs/headlessui/pull/3307), [#3309](https://github.com/tailwindlabs/headlessui/pull/3309), [#3312](https://github.com/tailwindlabs/headlessui/pull/3312))
- Add `DialogBackdrop` component ([#3307](https://github.com/tailwindlabs/headlessui/pull/3307), [#3310](https://github.com/tailwindlabs/headlessui/pull/3310))
- Add `PopoverBackdrop` component to replace `PopoverOverlay` ([#3308](https://github.com/tailwindlabs/headlessui/pull/3308))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import { useResolveButtonType } from '../../hooks/use-resolve-button-type'
import { useScrollLock } from '../../hooks/use-scroll-lock'
import { useSyncRefs } from '../../hooks/use-sync-refs'
import { useTrackedPointer } from '../../hooks/use-tracked-pointer'
import { useTransition, type TransitionData } from '../../hooks/use-transition'
import { transitionDataAttributes, useTransition } from '../../hooks/use-transition'
import { useTreeWalker } from '../../hooks/use-tree-walker'
import { useWatch } from '../../hooks/use-watch'
import { useDisabled } from '../../internal/disabled'
Expand Down Expand Up @@ -1564,7 +1564,7 @@ let DEFAULT_OPTIONS_TAG = 'div' as const
type OptionsRenderPropArg = {
open: boolean
option: unknown
} & TransitionData
}
type OptionsPropsWeControl = 'aria-labelledby' | 'aria-multiselectable' | 'role' | 'tabIndex'

let OptionsRenderFeatures = RenderFeatures.RenderStrategy | RenderFeatures.Static
Expand Down Expand Up @@ -1665,9 +1665,8 @@ function OptionsFn<TTag extends ElementType = typeof DEFAULT_OPTIONS_TAG>(
return {
open: data.comboboxState === ComboboxState.Open,
option: undefined,
...transitionData,
} satisfies OptionsRenderPropArg
}, [data.comboboxState, transitionData])
}, [data.comboboxState])

// When the user scrolls **using the mouse** (so scroll event isn't appropriate)
// we want to make sure that the current activation trigger is set to pointer.
Expand Down Expand Up @@ -1706,6 +1705,7 @@ function OptionsFn<TTag extends ElementType = typeof DEFAULT_OPTIONS_TAG>(
} as CSSProperties,
onWheel: data.activationTrigger === ActivationTrigger.Pointer ? undefined : handleWheel,
onMouseDown: handleMouseDown,
...transitionDataAttributes(transitionData),
})

// We should freeze when the combobox is visible but "closed". This means that
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { useEvent } from '../../hooks/use-event'
import { useId } from '../../hooks/use-id'
import { useResolveButtonType } from '../../hooks/use-resolve-button-type'
import { optionalRef, useSyncRefs } from '../../hooks/use-sync-refs'
import { useTransition, type TransitionData } from '../../hooks/use-transition'
import { transitionDataAttributes, useTransition } from '../../hooks/use-transition'
import { CloseProvider } from '../../internal/close-provider'
import {
OpenClosedProvider,
Expand Down Expand Up @@ -425,7 +425,7 @@ let DEFAULT_PANEL_TAG = 'div' as const
type PanelRenderPropArg = {
open: boolean
close: (focusableElement?: HTMLElement | MutableRefObject<HTMLElement | null>) => void
} & TransitionData
}
type DisclosurePanelPropsWeControl = never

let PanelRenderFeatures = RenderFeatures.RenderStrategy | RenderFeatures.Static
Expand Down Expand Up @@ -475,13 +475,13 @@ function PanelFn<TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(
return {
open: state.disclosureState === DisclosureStates.Open,
close,
...transitionData,
} satisfies PanelRenderPropArg
}, [state.disclosureState, close, transitionData])
}, [state.disclosureState, close])

let ourProps = {
ref: panelRef,
id,
...transitionDataAttributes(transitionData),
}

return (
Expand Down
8 changes: 4 additions & 4 deletions packages/@headlessui-react/src/components/listbox/listbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import { useScrollLock } from '../../hooks/use-scroll-lock'
import { useSyncRefs } from '../../hooks/use-sync-refs'
import { useTextValue } from '../../hooks/use-text-value'
import { useTrackedPointer } from '../../hooks/use-tracked-pointer'
import { useTransition, type TransitionData } from '../../hooks/use-transition'
import { transitionDataAttributes, useTransition } from '../../hooks/use-transition'
import { useDisabled } from '../../internal/disabled'
import {
FloatingProvider,
Expand Down Expand Up @@ -870,7 +870,7 @@ let SelectedOptionContext = createContext(false)
let DEFAULT_OPTIONS_TAG = 'div' as const
type OptionsRenderPropArg = {
open: boolean
} & TransitionData
}
type OptionsPropsWeControl =
| 'aria-activedescendant'
| 'aria-labelledby'
Expand Down Expand Up @@ -1090,9 +1090,8 @@ function OptionsFn<TTag extends ElementType = typeof DEFAULT_OPTIONS_TAG>(
let slot = useMemo(() => {
return {
open: data.listboxState === ListboxStates.Open,
...transitionData,
} satisfies OptionsRenderPropArg
}, [data.listboxState, transitionData])
}, [data.listboxState])

let ourProps = mergeProps(anchor ? getFloatingPanelProps() : {}, {
id,
Expand All @@ -1113,6 +1112,7 @@ function OptionsFn<TTag extends ElementType = typeof DEFAULT_OPTIONS_TAG>(
...style,
'--button-width': useElementSize(data.buttonRef, true).width,
} as CSSProperties,
...transitionDataAttributes(transitionData),
})

// We should freeze when the listbox is visible but "closed". This means that
Expand Down
8 changes: 4 additions & 4 deletions packages/@headlessui-react/src/components/menu/menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import { useScrollLock } from '../../hooks/use-scroll-lock'
import { useSyncRefs } from '../../hooks/use-sync-refs'
import { useTextValue } from '../../hooks/use-text-value'
import { useTrackedPointer } from '../../hooks/use-tracked-pointer'
import { useTransition, type TransitionData } from '../../hooks/use-transition'
import { transitionDataAttributes, useTransition } from '../../hooks/use-transition'
import { useTreeWalker } from '../../hooks/use-tree-walker'
import {
FloatingProvider,
Expand Down Expand Up @@ -565,7 +565,7 @@ function ButtonFn<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(
let DEFAULT_ITEMS_TAG = 'div' as const
type ItemsRenderPropArg = {
open: boolean
} & TransitionData
}
type ItemsPropsWeControl = 'aria-activedescendant' | 'aria-labelledby' | 'role' | 'tabIndex'

let ItemsRenderFeatures = RenderFeatures.RenderStrategy | RenderFeatures.Static
Expand Down Expand Up @@ -760,9 +760,8 @@ function ItemsFn<TTag extends ElementType = typeof DEFAULT_ITEMS_TAG>(
let slot = useMemo(() => {
return {
open: state.menuState === MenuStates.Open,
...transitionData,
} satisfies ItemsRenderPropArg
}, [state.menuState, transitionData])
}, [state.menuState])

let ourProps = mergeProps(anchor ? getFloatingPanelProps() : {}, {
'aria-activedescendant':
Expand All @@ -782,6 +781,7 @@ function ItemsFn<TTag extends ElementType = typeof DEFAULT_ITEMS_TAG>(
...style,
'--button-width': useElementSize(state.buttonRef, true).width,
} as CSSProperties,
...transitionDataAttributes(transitionData),
})

return (
Expand Down
14 changes: 7 additions & 7 deletions packages/@headlessui-react/src/components/popover/popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import { useMainTreeNode, useRootContainers } from '../../hooks/use-root-contain
import { useScrollLock } from '../../hooks/use-scroll-lock'
import { optionalRef, useSyncRefs } from '../../hooks/use-sync-refs'
import { Direction as TabDirection, useTabDirection } from '../../hooks/use-tab-direction'
import { useTransition, type TransitionData } from '../../hooks/use-transition'
import { transitionDataAttributes, useTransition } from '../../hooks/use-transition'
import { CloseProvider } from '../../internal/close-provider'
import {
FloatingProvider,
Expand Down Expand Up @@ -732,7 +732,7 @@ function ButtonFn<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(
let DEFAULT_BACKDROP_TAG = 'div' as const
type BackdropRenderPropArg = {
open: boolean
} & TransitionData
}
type BackdropPropsWeControl = 'aria-hidden'

let BackdropRenderFeatures = RenderFeatures.RenderStrategy | RenderFeatures.Static
Expand Down Expand Up @@ -778,15 +778,15 @@ function BackdropFn<TTag extends ElementType = typeof DEFAULT_BACKDROP_TAG>(
let slot = useMemo(() => {
return {
open: popoverState === PopoverStates.Open,
...transitionData,
} satisfies BackdropRenderPropArg
}, [popoverState, transitionData])
}, [popoverState])

let ourProps = {
ref: backdropRef,
id,
'aria-hidden': true,
onClick: handleClick,
...transitionDataAttributes(transitionData),
}

return render({
Expand All @@ -806,7 +806,7 @@ let DEFAULT_PANEL_TAG = 'div' as const
type PanelRenderPropArg = {
open: boolean
close: (focusableElement?: HTMLElement | MutableRefObject<HTMLElement | null>) => void
} & TransitionData
}

let PanelRenderFeatures = RenderFeatures.RenderStrategy | RenderFeatures.Static

Expand Down Expand Up @@ -936,9 +936,8 @@ function PanelFn<TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(
return {
open: state.popoverState === PopoverStates.Open,
close,
...transitionData,
} satisfies PanelRenderPropArg
}, [state.popoverState, close, transitionData])
}, [state.popoverState, close])

let ourProps: Record<string, any> = mergeProps(anchor ? getFloatingPanelProps() : {}, {
ref: panelRef,
Expand Down Expand Up @@ -968,6 +967,7 @@ function PanelFn<TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(
...style,
'--button-width': useElementSize(state.button, true).width,
} as React.CSSProperties,
...transitionDataAttributes(transitionData),
})

let direction = useTabDirection()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ exports[`Setup API transition classes should be possible to passthrough the tran
class="enter enter-from"
data-closed=""
data-enter=""
data-headlessui-state="closed enter transition"
data-transition=""
style=""
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,6 @@ describe('Setup API', () => {
<div
class="foo1
foo2 leave"
data-headlessui-state="leave transition"
data-leave=""
data-transition=""
style=""
Expand Down
24 changes: 12 additions & 12 deletions packages/@headlessui-react/src/components/transition/transition.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { useLatestValue } from '../../hooks/use-latest-value'
import { useOnDisappear } from '../../hooks/use-on-disappear'
import { useServerHandoffComplete } from '../../hooks/use-server-handoff-complete'
import { useSyncRefs } from '../../hooks/use-sync-refs'
import { useTransition } from '../../hooks/use-transition'
import { transitionDataAttributes, useTransition } from '../../hooks/use-transition'
import { OpenClosedProvider, State, useOpenClosed } from '../../internal/open-closed'
import type { Props, ReactTag } from '../../types'
import { classNames } from '../../utils/class-names'
Expand Down Expand Up @@ -437,7 +437,7 @@ function TransitionChildFn<TTag extends ElementType = typeof DEFAULT_TRANSITION_
// a leave transition on the `<Transition>` is done, but there is still a
// child `<TransitionChild>` busy, then `visible` would be `false`, while
// `state` would still be `TreeStates.Visible`.
let [, slot] = useTransition(enabled, container, show, { start, end })
let [, transitionData] = useTransition(enabled, container, show, { start, end })

let ourProps = compact({
ref: transitionRef,
Expand All @@ -451,33 +451,33 @@ function TransitionChildFn<TTag extends ElementType = typeof DEFAULT_TRANSITION_
immediate && enterFrom,

// Map data attributes to `enter`, `enterFrom` and `enterTo` classes
slot.enter && enter,
slot.enter && slot.closed && enterFrom,
slot.enter && !slot.closed && enterTo,
transitionData.enter && enter,
transitionData.enter && transitionData.closed && enterFrom,
transitionData.enter && !transitionData.closed && enterTo,

// Map data attributes to `leave`, `leaveFrom` and `leaveTo` classes
slot.leave && leave,
slot.leave && !slot.closed && leaveFrom,
slot.leave && slot.closed && leaveTo,
transitionData.leave && leave,
transitionData.leave && !transitionData.closed && leaveFrom,
transitionData.leave && transitionData.closed && leaveTo,

// Map data attributes to `entered` class (backwards compatibility)
!slot.transition && show && entered
!transitionData.transition && show && entered
)?.trim() || undefined, // If `className` is an empty string, we can omit it
...transitionDataAttributes(transitionData),
})

let openClosedState = 0
if (state === TreeStates.Visible) openClosedState |= State.Open
if (state === TreeStates.Hidden) openClosedState |= State.Closed
if (slot.enter) openClosedState |= State.Opening
if (slot.leave) openClosedState |= State.Closing
if (transitionData.enter) openClosedState |= State.Opening
if (transitionData.leave) openClosedState |= State.Closing

return (
<NestingContext.Provider value={nesting}>
<OpenClosedProvider value={openClosedState}>
{render({
ourProps,
theirProps,
slot,
defaultTag: DEFAULT_TRANSITION_CHILD_TAG,
features: TransitionChildRenderFeatures,
visible: state === TreeStates.Visible,
Expand Down
12 changes: 11 additions & 1 deletion packages/@headlessui-react/src/hooks/use-transition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,23 @@ enum TransitionState {
Leave = 1 << 2,
}

export type TransitionData = {
type TransitionData = {
closed?: boolean
enter?: boolean
leave?: boolean
transition?: boolean
}

export function transitionDataAttributes(data: TransitionData) {
let attributes: Record<string, string> = {}
for (let key in data) {
if (data[key as keyof TransitionData] === true) {
attributes[`data-${key}`] = ''
}
}
return attributes
}

export function useTransition(
enabled: boolean,
elementRef: MutableRefObject<HTMLElement | null>,
Expand Down
Loading