Fix crash in ListboxOptions when using as={Fragment} (#3513)
This PR fixes an issue where a `Maximum update depth exceeded` error
occurs if you use `as={Fragment}` in the `ListboxOptions` component.
This PR also includes a refactor to make sure this exact issue cannot
happen anymore in other components.
Fixes: #3507
This commit is contained in:
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
### Fixed
|
||||
|
||||
- Use `React.JSX` instead of deprecated global `JSX` ([#3511](https://github.com/tailwindlabs/headlessui/pull/3511))
|
||||
- Fix crash in `ListboxOptions` when using `as={Fragment}` ([#3513](https://github.com/tailwindlabs/headlessui/pull/3513))
|
||||
|
||||
## [2.1.9] - 2024-10-03
|
||||
|
||||
|
||||
@@ -9,8 +9,7 @@ import type { Props } from '../../types'
|
||||
import {
|
||||
forwardRefWithAs,
|
||||
mergeProps,
|
||||
render,
|
||||
useMergeRefsFn,
|
||||
useRender,
|
||||
type HasDisplayName,
|
||||
type RefProp,
|
||||
} from '../../utils/render'
|
||||
@@ -42,7 +41,6 @@ function ButtonFn<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(
|
||||
ref: Ref<HTMLElement>
|
||||
) {
|
||||
let providedDisabled = useDisabled()
|
||||
let mergeRefs = useMergeRefsFn()
|
||||
let { disabled = providedDisabled || false, autoFocus = false, ...theirProps } = props
|
||||
|
||||
let { isFocusVisible: focus, focusProps } = useFocusRing({ autoFocus })
|
||||
@@ -65,8 +63,9 @@ function ButtonFn<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(
|
||||
return { disabled, hover, focus, active, autofocus: autoFocus } satisfies ButtonRenderPropArg
|
||||
}, [disabled, hover, focus, active, autoFocus])
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return render({
|
||||
mergeRefs,
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
|
||||
@@ -26,7 +26,7 @@ import { attemptSubmit } from '../../utils/form'
|
||||
import {
|
||||
forwardRefWithAs,
|
||||
mergeProps,
|
||||
render,
|
||||
useRender,
|
||||
type HasDisplayName,
|
||||
type RefProp,
|
||||
} from '../../utils/render'
|
||||
@@ -176,6 +176,8 @@ function CheckboxFn<TTag extends ElementType = typeof DEFAULT_CHECKBOX_TAG, TTyp
|
||||
return onChange?.(defaultChecked)
|
||||
}, [onChange, defaultChecked])
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<>
|
||||
{name != null && (
|
||||
|
||||
@@ -69,8 +69,7 @@ import {
|
||||
RenderFeatures,
|
||||
forwardRefWithAs,
|
||||
mergeProps,
|
||||
render,
|
||||
useMergeRefsFn,
|
||||
useRender,
|
||||
type HasDisplayName,
|
||||
type PropsForFeatures,
|
||||
type RefProp,
|
||||
@@ -949,6 +948,8 @@ function ComboboxFn<TValue, TTag extends ElementType = typeof DEFAULT_COMBOBOX_T
|
||||
return theirOnChange?.(defaultValue)
|
||||
}, [theirOnChange, defaultValue])
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<LabelProvider
|
||||
value={labelledby}
|
||||
@@ -1444,6 +1445,8 @@ function InputFn<
|
||||
hoverProps
|
||||
)
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return render({
|
||||
ourProps,
|
||||
theirProps,
|
||||
@@ -1489,7 +1492,6 @@ function ButtonFn<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(
|
||||
let data = useData('Combobox.Button')
|
||||
let actions = useActions('Combobox.Button')
|
||||
let buttonRef = useSyncRefs(ref, actions.setButtonElement)
|
||||
let mergeRefs = useMergeRefsFn()
|
||||
|
||||
let internalId = useId()
|
||||
let {
|
||||
@@ -1610,8 +1612,9 @@ function ButtonFn<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(
|
||||
pressProps
|
||||
)
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return render({
|
||||
mergeRefs,
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
@@ -1813,6 +1816,8 @@ function OptionsFn<TTag extends ElementType = typeof DEFAULT_OPTIONS_TAG>(
|
||||
})
|
||||
}
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<Portal enabled={portal ? props.static || visible : false}>
|
||||
<ComboboxDataContext.Provider
|
||||
@@ -2037,6 +2042,8 @@ function OptionFn<
|
||||
onMouseLeave: handleLeave,
|
||||
}
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return render({
|
||||
ourProps,
|
||||
theirProps,
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { Props } from '../../types'
|
||||
import {
|
||||
forwardRefWithAs,
|
||||
mergeProps,
|
||||
render,
|
||||
useRender,
|
||||
type HasDisplayName,
|
||||
type RefProp,
|
||||
} from '../../utils/render'
|
||||
@@ -47,6 +47,8 @@ function DataInteractiveFn<TTag extends ElementType = typeof DEFAULT_DATA_INTERA
|
||||
[hover, focus, active]
|
||||
)
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return render({
|
||||
ourProps,
|
||||
theirProps,
|
||||
|
||||
@@ -15,7 +15,7 @@ import { useIsoMorphicEffect } from '../../hooks/use-iso-morphic-effect'
|
||||
import { useSyncRefs } from '../../hooks/use-sync-refs'
|
||||
import { useDisabled } from '../../internal/disabled'
|
||||
import type { Props } from '../../types'
|
||||
import { forwardRefWithAs, render, type HasDisplayName, type RefProp } from '../../utils/render'
|
||||
import { forwardRefWithAs, useRender, type HasDisplayName, type RefProp } from '../../utils/render'
|
||||
|
||||
// ---
|
||||
|
||||
@@ -121,6 +121,8 @@ function DescriptionFn<TTag extends ElementType = typeof DEFAULT_DESCRIPTION_TAG
|
||||
let slot = useMemo(() => ({ ...context.slot, disabled }), [context.slot, disabled])
|
||||
let ourProps = { ref: descriptionRef, ...context.props, id }
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return render({
|
||||
ourProps,
|
||||
theirProps,
|
||||
|
||||
@@ -41,7 +41,7 @@ import { match } from '../../utils/match'
|
||||
import {
|
||||
RenderFeatures,
|
||||
forwardRefWithAs,
|
||||
render,
|
||||
useRender,
|
||||
type HasDisplayName,
|
||||
type PropsForFeatures,
|
||||
type RefProp,
|
||||
@@ -286,6 +286,8 @@ let InternalDialog = forwardRefWithAs(function InternalDialog<
|
||||
}
|
||||
}
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<ResetOpenClosedProvider>
|
||||
<ForcePortalRoot force={true}>
|
||||
@@ -450,6 +452,8 @@ function PanelFn<TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(
|
||||
let Wrapper = transition ? TransitionChild : Fragment
|
||||
let wrapperProps = transition ? { unmount } : {}
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<Wrapper {...wrapperProps}>
|
||||
{render({
|
||||
@@ -494,6 +498,8 @@ function BackdropFn<TTag extends ElementType = typeof DEFAULT_BACKDROP_TAG>(
|
||||
let Wrapper = transition ? TransitionChild : Fragment
|
||||
let wrapperProps = transition ? { unmount } : {}
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<Wrapper {...wrapperProps}>
|
||||
{render({
|
||||
@@ -541,6 +547,8 @@ function TitleFn<TTag extends ElementType = typeof DEFAULT_TITLE_TAG>(
|
||||
|
||||
let ourProps = { ref: titleRef, id }
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return render({
|
||||
ourProps,
|
||||
theirProps,
|
||||
|
||||
@@ -41,8 +41,7 @@ import {
|
||||
RenderFeatures,
|
||||
forwardRefWithAs,
|
||||
mergeProps,
|
||||
render,
|
||||
useMergeRefsFn,
|
||||
useRender,
|
||||
type HasDisplayName,
|
||||
type PropsForFeatures,
|
||||
type RefProp,
|
||||
@@ -233,6 +232,8 @@ function DisclosureFn<TTag extends ElementType = typeof DEFAULT_DISCLOSURE_TAG>(
|
||||
ref: disclosureRef,
|
||||
}
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<DisclosureContext.Provider value={reducerBag}>
|
||||
<DisclosureAPIContext.Provider value={api}>
|
||||
@@ -304,7 +305,6 @@ function ButtonFn<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(
|
||||
return dispatch({ type: ActionTypes.SetButtonElement, element })
|
||||
})
|
||||
)
|
||||
let mergeRefs = useMergeRefsFn()
|
||||
|
||||
useEffect(() => {
|
||||
if (isWithinPanel) return
|
||||
@@ -411,8 +411,9 @@ function ButtonFn<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(
|
||||
pressProps
|
||||
)
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return render({
|
||||
mergeRefs,
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
@@ -451,7 +452,6 @@ function PanelFn<TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(
|
||||
} = props
|
||||
let [state, dispatch] = useDisclosureContext('Disclosure.Panel')
|
||||
let { close } = useDisclosureAPIContext('Disclosure.Panel')
|
||||
let mergeRefs = useMergeRefsFn()
|
||||
|
||||
// To improve the correctness of transitions (timing related race conditions),
|
||||
// we track the element locally to this component, instead of relying on the
|
||||
@@ -496,11 +496,12 @@ function PanelFn<TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(
|
||||
...transitionDataAttributes(transitionData),
|
||||
}
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<ResetOpenClosedProvider>
|
||||
<DisclosurePanelContext.Provider value={state.panelId}>
|
||||
{render({
|
||||
mergeRefs,
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
|
||||
@@ -6,7 +6,7 @@ import { DisabledProvider, useDisabled } from '../../internal/disabled'
|
||||
import { FormFieldsProvider } from '../../internal/form-fields'
|
||||
import { IdProvider } from '../../internal/id'
|
||||
import type { Props } from '../../types'
|
||||
import { forwardRefWithAs, render, type HasDisplayName } from '../../utils/render'
|
||||
import { forwardRefWithAs, useRender, type HasDisplayName } from '../../utils/render'
|
||||
import { useDescriptions } from '../description/description'
|
||||
import { useLabels } from '../label/label'
|
||||
|
||||
@@ -44,6 +44,8 @@ function FieldFn<TTag extends ElementType = typeof DEFAULT_FIELD_TAG>(
|
||||
'aria-disabled': disabled || undefined,
|
||||
}
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<DisabledProvider value={disabled}>
|
||||
<LabelProvider value={labelledby}>
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useResolvedTag } from '../../hooks/use-resolved-tag'
|
||||
import { useSyncRefs } from '../../hooks/use-sync-refs'
|
||||
import { DisabledProvider, useDisabled } from '../../internal/disabled'
|
||||
import type { Props } from '../../types'
|
||||
import { forwardRefWithAs, render, type HasDisplayName } from '../../utils/render'
|
||||
import { forwardRefWithAs, useRender, type HasDisplayName } from '../../utils/render'
|
||||
import { useLabels } from '../label/label'
|
||||
|
||||
let DEFAULT_FIELDSET_TAG = 'fieldset' as const
|
||||
@@ -50,6 +50,8 @@ function FieldsetFn<TTag extends ElementType = typeof DEFAULT_FIELDSET_TAG>(
|
||||
'aria-disabled': disabled || undefined,
|
||||
}
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<DisabledProvider value={disabled}>
|
||||
<LabelProvider>
|
||||
|
||||
@@ -24,7 +24,7 @@ import { history } from '../../utils/active-element-history'
|
||||
import { Focus, FocusResult, focusElement, focusIn } from '../../utils/focus-management'
|
||||
import { match } from '../../utils/match'
|
||||
import { microTask } from '../../utils/micro-task'
|
||||
import { forwardRefWithAs, render, type HasDisplayName, type RefProp } from '../../utils/render'
|
||||
import { forwardRefWithAs, useRender, type HasDisplayName, type RefProp } from '../../utils/render'
|
||||
|
||||
type Containers =
|
||||
// Lazy resolved containers
|
||||
@@ -197,6 +197,8 @@ function FocusTrapFn<TTag extends ElementType = typeof DEFAULT_FOCUS_TRAP_TAG>(
|
||||
},
|
||||
}
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<>
|
||||
{tabLockEnabled && (
|
||||
|
||||
@@ -10,7 +10,7 @@ import type { Props } from '../../types'
|
||||
import {
|
||||
forwardRefWithAs,
|
||||
mergeProps,
|
||||
render,
|
||||
useRender,
|
||||
type HasDisplayName,
|
||||
type RefProp,
|
||||
} from '../../utils/render'
|
||||
@@ -78,6 +78,8 @@ function InputFn<TTag extends ElementType = typeof DEFAULT_INPUT_TAG>(
|
||||
return { disabled, invalid, hover, focus, autofocus: autoFocus } satisfies InputRenderPropArg
|
||||
}, [disabled, invalid, hover, focus, autoFocus])
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return render({
|
||||
ourProps,
|
||||
theirProps,
|
||||
|
||||
@@ -17,7 +17,7 @@ import { useSyncRefs } from '../../hooks/use-sync-refs'
|
||||
import { useDisabled } from '../../internal/disabled'
|
||||
import { useProvidedId } from '../../internal/id'
|
||||
import type { Props } from '../../types'
|
||||
import { forwardRefWithAs, render, type HasDisplayName, type RefProp } from '../../utils/render'
|
||||
import { forwardRefWithAs, useRender, type HasDisplayName, type RefProp } from '../../utils/render'
|
||||
|
||||
// ---
|
||||
|
||||
@@ -203,6 +203,8 @@ function LabelFn<TTag extends ElementType = typeof DEFAULT_LABEL_TAG>(
|
||||
}
|
||||
}
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return render({
|
||||
ourProps,
|
||||
theirProps,
|
||||
|
||||
@@ -74,8 +74,7 @@ import {
|
||||
RenderFeatures,
|
||||
forwardRefWithAs,
|
||||
mergeProps,
|
||||
render,
|
||||
useMergeRefsFn,
|
||||
useRender,
|
||||
type HasDisplayName,
|
||||
type PropsForFeatures,
|
||||
type RefProp,
|
||||
@@ -698,6 +697,8 @@ function ListboxFn<
|
||||
return theirOnChange?.(defaultValue)
|
||||
}, [theirOnChange, defaultValue])
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<LabelProvider
|
||||
value={labelledby}
|
||||
@@ -786,7 +787,6 @@ function ButtonFn<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(
|
||||
autoFocus = false,
|
||||
...theirProps
|
||||
} = props
|
||||
let mergeRefs = useMergeRefsFn()
|
||||
let buttonRef = useSyncRefs(ref, useFloatingReference(), actions.setButtonElement)
|
||||
let getFloatingReferenceProps = useFloatingReferenceProps()
|
||||
|
||||
@@ -881,8 +881,9 @@ function ButtonFn<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(
|
||||
pressProps
|
||||
)
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return render({
|
||||
mergeRefs,
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
@@ -1159,6 +1160,8 @@ function OptionsFn<TTag extends ElementType = typeof DEFAULT_OPTIONS_TAG>(
|
||||
...transitionDataAttributes(transitionData),
|
||||
})
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<Portal enabled={portal ? props.static || visible : false}>
|
||||
<ListboxDataContext.Provider
|
||||
@@ -1336,6 +1339,8 @@ function OptionFn<
|
||||
}
|
||||
: {}
|
||||
|
||||
let render = useRender()
|
||||
|
||||
if (!selected && usedInSelectedOption) {
|
||||
return null
|
||||
}
|
||||
@@ -1383,6 +1388,8 @@ function SelectedFn<TTag extends ElementType = typeof DEFAULT_SELECTED_OPTION_TA
|
||||
data.value === null ||
|
||||
(data.mode === ValueMode.Multi && Array.isArray(data.value) && data.value.length === 0)
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<SelectedOptionContext.Provider value={true}>
|
||||
{render({
|
||||
|
||||
@@ -67,8 +67,7 @@ import {
|
||||
RenderFeatures,
|
||||
forwardRefWithAs,
|
||||
mergeProps,
|
||||
render,
|
||||
useMergeRefsFn,
|
||||
useRender,
|
||||
type HasDisplayName,
|
||||
type RefProp,
|
||||
} from '../../utils/render'
|
||||
@@ -426,6 +425,8 @@ function MenuFn<TTag extends ElementType = typeof DEFAULT_MENU_TAG>(
|
||||
|
||||
let ourProps = { ref: menuRef }
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<FloatingProvider>
|
||||
<MenuContext.Provider value={reducerBag}>
|
||||
@@ -484,7 +485,6 @@ function ButtonFn<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(
|
||||
} = props
|
||||
let [state, dispatch] = useMenuContext('Menu.Button')
|
||||
let getFloatingReferenceProps = useFloatingReferenceProps()
|
||||
let mergeRefs = useMergeRefsFn()
|
||||
let buttonRef = useSyncRefs(
|
||||
ref,
|
||||
useFloatingReference(),
|
||||
@@ -571,8 +571,9 @@ function ButtonFn<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(
|
||||
pressProps
|
||||
)
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return render({
|
||||
mergeRefs,
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
@@ -820,6 +821,8 @@ function ItemsFn<TTag extends ElementType = typeof DEFAULT_ITEMS_TAG>(
|
||||
...transitionDataAttributes(transitionData),
|
||||
})
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<Portal enabled={portal ? props.static || visible : false}>
|
||||
{render({
|
||||
@@ -982,6 +985,8 @@ function ItemFn<TTag extends ElementType = typeof DEFAULT_ITEM_TAG>(
|
||||
onMouseLeave: handleLeave,
|
||||
}
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<LabelProvider>
|
||||
<DescriptionProvider>
|
||||
@@ -1018,6 +1023,8 @@ function SectionFn<TTag extends ElementType = typeof DEFAULT_SECTION_TAG>(
|
||||
let theirProps = props
|
||||
let ourProps = { ref, 'aria-labelledby': labelledby, role: 'group' }
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<LabelProvider>
|
||||
{render({
|
||||
@@ -1055,6 +1062,8 @@ function HeadingFn<TTag extends ElementType = typeof DEFAULT_HEADING_TAG>(
|
||||
|
||||
let ourProps = { id, ref, role: 'presentation', ...context.props }
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return render({
|
||||
ourProps,
|
||||
theirProps,
|
||||
@@ -1083,6 +1092,8 @@ function SeparatorFn<TTag extends ElementType = typeof DEFAULT_SEPARATOR_TAG>(
|
||||
let theirProps = props
|
||||
let ourProps = { ref, role: 'separator' }
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return render({
|
||||
ourProps,
|
||||
theirProps,
|
||||
|
||||
@@ -74,8 +74,7 @@ import {
|
||||
RenderFeatures,
|
||||
forwardRefWithAs,
|
||||
mergeProps,
|
||||
render,
|
||||
useMergeRefsFn,
|
||||
useRender,
|
||||
type HasDisplayName,
|
||||
type PropsForFeatures,
|
||||
type RefProp,
|
||||
@@ -419,6 +418,8 @@ function PopoverFn<TTag extends ElementType = typeof DEFAULT_POPOVER_TAG>(
|
||||
|
||||
let ourProps = { ref: popoverRef }
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<MainTreeProvider node={mainTreeNode}>
|
||||
<FloatingProvider>
|
||||
@@ -707,6 +708,8 @@ function ButtonFn<TTag extends ElementType = typeof DEFAULT_BUTTON_TAG>(
|
||||
}
|
||||
})
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<>
|
||||
{render({
|
||||
@@ -799,6 +802,8 @@ function BackdropFn<TTag extends ElementType = typeof DEFAULT_BACKDROP_TAG>(
|
||||
...transitionDataAttributes(transitionData),
|
||||
}
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return render({
|
||||
ourProps,
|
||||
theirProps,
|
||||
@@ -884,7 +889,6 @@ function PanelFn<TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(
|
||||
setLocalPanelElement
|
||||
)
|
||||
let ownerDocument = useOwnerDocument(internalPanelRef)
|
||||
let mergeRefs = useMergeRefsFn()
|
||||
|
||||
useIsoMorphicEffect(() => {
|
||||
dispatch({ type: ActionTypes.SetPanelId, panelId: id })
|
||||
@@ -1070,6 +1074,8 @@ function PanelFn<TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(
|
||||
}
|
||||
})
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<ResetOpenClosedProvider>
|
||||
<PopoverPanelContext.Provider value={id}>
|
||||
@@ -1087,7 +1093,6 @@ function PanelFn<TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(
|
||||
/>
|
||||
)}
|
||||
{render({
|
||||
mergeRefs,
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
@@ -1188,6 +1193,8 @@ function GroupFn<TTag extends ElementType = typeof DEFAULT_GROUP_TAG>(
|
||||
let theirProps = props
|
||||
let ourProps = { ref: groupRef }
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<MainTreeProvider>
|
||||
<PopoverGroupContext.Provider value={contextBag}>
|
||||
|
||||
@@ -23,7 +23,7 @@ import { optionalRef, useSyncRefs } from '../../hooks/use-sync-refs'
|
||||
import { usePortalRoot } from '../../internal/portal-force-root'
|
||||
import type { Props } from '../../types'
|
||||
import { env } from '../../utils/env'
|
||||
import { forwardRefWithAs, render, type HasDisplayName, type RefProp } from '../../utils/render'
|
||||
import { forwardRefWithAs, useRender, type HasDisplayName, type RefProp } from '../../utils/render'
|
||||
|
||||
function usePortalTarget(ref: MutableRefObject<HTMLElement | null>): HTMLElement | null {
|
||||
let forceInRoot = usePortalRoot()
|
||||
@@ -129,6 +129,7 @@ let InternalPortalFn = forwardRefWithAs(function InternalPortalFn<
|
||||
}
|
||||
})
|
||||
|
||||
let render = useRender()
|
||||
if (!ready) return null
|
||||
|
||||
let ourProps = { ref: portalRef }
|
||||
@@ -154,6 +155,9 @@ function PortalFn<TTag extends ElementType = typeof DEFAULT_PORTAL_TAG>(
|
||||
let portalRef = useSyncRefs(ref)
|
||||
|
||||
let { enabled = true, ...theirProps } = props
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return enabled ? (
|
||||
<InternalPortalFn {...theirProps} ref={portalRef} />
|
||||
) : (
|
||||
@@ -193,6 +197,8 @@ function GroupFn<TTag extends ElementType = typeof DEFAULT_GROUP_TAG>(
|
||||
|
||||
let ourProps = { ref: groupRef }
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<PortalGroupContext.Provider value={target}>
|
||||
{render({
|
||||
|
||||
@@ -35,7 +35,7 @@ import { getOwnerDocument } from '../../utils/owner'
|
||||
import {
|
||||
forwardRefWithAs,
|
||||
mergeProps,
|
||||
render,
|
||||
useRender,
|
||||
type HasDisplayName,
|
||||
type RefProp,
|
||||
} from '../../utils/render'
|
||||
@@ -309,6 +309,8 @@ function RadioGroupFn<TTag extends ElementType = typeof DEFAULT_RADIO_GROUP_TAG,
|
||||
return triggerChange(defaultValue)
|
||||
}, [triggerChange, defaultValue])
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<DescriptionProvider name="RadioGroup.Description">
|
||||
<LabelProvider name="RadioGroup.Label">
|
||||
@@ -444,6 +446,8 @@ function OptionFn<
|
||||
} satisfies OptionRenderPropArg
|
||||
}, [checked, disabled, hover, focus, autoFocus])
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<DescriptionProvider name="RadioGroup.Description">
|
||||
<LabelProvider name="RadioGroup.Label">
|
||||
@@ -557,6 +561,8 @@ function RadioFn<
|
||||
return { checked, disabled, hover, focus, autofocus: autoFocus } satisfies RadioRenderPropArg
|
||||
}, [checked, disabled, hover, focus, autoFocus])
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return render({
|
||||
ourProps,
|
||||
theirProps,
|
||||
|
||||
@@ -11,7 +11,7 @@ import type { Props } from '../../types'
|
||||
import {
|
||||
forwardRefWithAs,
|
||||
mergeProps,
|
||||
render,
|
||||
useRender,
|
||||
type HasDisplayName,
|
||||
type RefProp,
|
||||
} from '../../utils/render'
|
||||
@@ -89,6 +89,8 @@ function SelectFn<TTag extends ElementType = typeof DEFAULT_SELECT_TAG>(
|
||||
} satisfies SelectRenderPropArg
|
||||
}, [disabled, invalid, hover, focus, active, autoFocus])
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return render({
|
||||
ourProps,
|
||||
theirProps,
|
||||
|
||||
@@ -32,7 +32,7 @@ import { attemptSubmit } from '../../utils/form'
|
||||
import {
|
||||
forwardRefWithAs,
|
||||
mergeProps,
|
||||
render,
|
||||
useRender,
|
||||
type HasDisplayName,
|
||||
type RefProp,
|
||||
} from '../../utils/render'
|
||||
@@ -74,6 +74,8 @@ function GroupFn<TTag extends ElementType = typeof DEFAULT_GROUP_TAG>(
|
||||
let ourProps = {}
|
||||
let theirProps = props
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<DescriptionProvider name="Switch.Description" value={describedby}>
|
||||
<LabelProvider
|
||||
@@ -244,6 +246,8 @@ function SwitchFn<TTag extends ElementType = typeof DEFAULT_SWITCH_TAG>(
|
||||
return onChange?.(defaultChecked)
|
||||
}, [onChange, defaultChecked])
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<>
|
||||
{name != null && (
|
||||
|
||||
@@ -33,7 +33,7 @@ import {
|
||||
RenderFeatures,
|
||||
forwardRefWithAs,
|
||||
mergeProps,
|
||||
render,
|
||||
useRender,
|
||||
type HasDisplayName,
|
||||
type PropsForFeatures,
|
||||
type RefProp,
|
||||
@@ -320,6 +320,8 @@ function GroupFn<TTag extends ElementType = typeof DEFAULT_TABS_TAG>(
|
||||
|
||||
let ourProps = { ref: tabsRef }
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<StableCollection>
|
||||
<TabsActionsContext.Provider value={tabsActions}>
|
||||
@@ -384,6 +386,8 @@ function ListFn<TTag extends ElementType = typeof DEFAULT_LIST_TAG>(
|
||||
'aria-orientation': orientation,
|
||||
}
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return render({
|
||||
ourProps,
|
||||
theirProps,
|
||||
@@ -556,6 +560,8 @@ function TabFn<TTag extends ElementType = typeof DEFAULT_TAB_TAG>(
|
||||
pressProps
|
||||
)
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return render({
|
||||
ourProps,
|
||||
theirProps,
|
||||
@@ -589,6 +595,8 @@ function PanelsFn<TTag extends ElementType = typeof DEFAULT_PANELS_TAG>(
|
||||
let theirProps = props
|
||||
let ourProps = { ref: panelsRef }
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return render({
|
||||
ourProps,
|
||||
theirProps,
|
||||
@@ -650,6 +658,8 @@ function PanelFn<TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(
|
||||
focusProps
|
||||
)
|
||||
|
||||
let render = useRender()
|
||||
|
||||
if (!selected && (theirProps.unmount ?? true) && !(theirProps.static ?? false)) {
|
||||
return <Hidden aria-hidden="true" {...ourProps} />
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import type { Props } from '../../types'
|
||||
import {
|
||||
forwardRefWithAs,
|
||||
mergeProps,
|
||||
render,
|
||||
useRender,
|
||||
type HasDisplayName,
|
||||
type RefProp,
|
||||
} from '../../utils/render'
|
||||
@@ -78,6 +78,8 @@ function TextareaFn<TTag extends ElementType = typeof DEFAULT_TEXTAREA_TAG>(
|
||||
return { disabled, invalid, hover, focus, autofocus: autoFocus } satisfies TextareaRenderPropArg
|
||||
}, [disabled, invalid, hover, focus, autoFocus])
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return render({
|
||||
ourProps,
|
||||
theirProps,
|
||||
|
||||
@@ -33,7 +33,7 @@ import {
|
||||
RenderFeatures,
|
||||
forwardRefWithAs,
|
||||
mergeProps,
|
||||
render,
|
||||
useRender,
|
||||
type HasDisplayName,
|
||||
type PropsForFeatures,
|
||||
type RefProp,
|
||||
@@ -290,6 +290,8 @@ function TooltipFn<TTag extends ElementType = typeof DEFAULT_TOOLTIP_TAG>(
|
||||
)
|
||||
let actions = useMemo<_Actions>(() => ({ showTooltip, hideTooltip }), [showTooltip, hideTooltip])
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<DescriptionProvider value={describedBy}>
|
||||
<FloatingProvider>
|
||||
@@ -395,6 +397,8 @@ function TriggerFn<TTag extends ElementType = typeof DEFAULT_TRIGGER_TAG>(
|
||||
hoverProps
|
||||
)
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return render({
|
||||
ourProps,
|
||||
theirProps,
|
||||
@@ -448,6 +452,8 @@ function PanelFn<TTag extends ElementType = typeof DEFAULT_PANEL_TAG>(
|
||||
|
||||
let slot = useMemo(() => ({}) satisfies PanelRenderPropArg, [])
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return render({
|
||||
ourProps: {
|
||||
...ourProps,
|
||||
|
||||
@@ -29,7 +29,7 @@ import {
|
||||
RenderStrategy,
|
||||
compact,
|
||||
forwardRefWithAs,
|
||||
render,
|
||||
useRender,
|
||||
type HasDisplayName,
|
||||
type PropsForFeatures,
|
||||
type RefProp,
|
||||
@@ -475,6 +475,8 @@ function TransitionChildFn<TTag extends ElementType = typeof DEFAULT_TRANSITION_
|
||||
if (transitionData.enter) openClosedState |= State.Opening
|
||||
if (transitionData.leave) openClosedState |= State.Closing
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<NestingContext.Provider value={nesting}>
|
||||
<OpenClosedProvider value={openClosedState}>
|
||||
@@ -571,6 +573,8 @@ function TransitionRootFn<TTag extends ElementType = typeof DEFAULT_TRANSITION_C
|
||||
props.beforeLeave?.()
|
||||
})
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return (
|
||||
<NestingContext.Provider value={nestingBag}>
|
||||
<TransitionContext.Provider value={transitionBag}>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { ElementType, Ref } from 'react'
|
||||
import type { Props } from '../types'
|
||||
import { forwardRefWithAs, render, type HasDisplayName, type RefProp } from '../utils/render'
|
||||
import { forwardRefWithAs, useRender, type HasDisplayName, type RefProp } from '../utils/render'
|
||||
|
||||
let DEFAULT_VISUALLY_HIDDEN_TAG = 'span' as const
|
||||
|
||||
@@ -55,6 +55,8 @@ function VisuallyHidden<TTag extends ElementType = typeof DEFAULT_VISUALLY_HIDDE
|
||||
},
|
||||
}
|
||||
|
||||
let render = useRender()
|
||||
|
||||
return render({
|
||||
ourProps,
|
||||
theirProps,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { getByTestId, prettyDOM, render as testRender } from '@testing-library/r
|
||||
import React, { Fragment, createRef, type ElementType, type Ref } from 'react'
|
||||
import { suppressConsoleLogs } from '../test-utils/suppress-console-logs'
|
||||
import type { Expand, Props } from '../types'
|
||||
import { RenderFeatures, render, type PropsForFeatures } from './render'
|
||||
import { RenderFeatures, useRender, type PropsForFeatures } from './render'
|
||||
|
||||
function contents(id = 'wrapper') {
|
||||
return prettyDOM(getByTestId(document.body, id), undefined, {
|
||||
@@ -15,6 +15,7 @@ describe('Default functionality', () => {
|
||||
function Dummy<TTag extends ElementType = 'div'>(
|
||||
props: Props<TTag> & Partial<{ a: any; b: any; c: any }>
|
||||
) {
|
||||
let render = useRender()
|
||||
return (
|
||||
<div data-testid="wrapper">
|
||||
{render({
|
||||
@@ -31,6 +32,7 @@ describe('Default functionality', () => {
|
||||
function DummyWithClassName<TTag extends ElementType = 'div'>(
|
||||
props: Props<TTag> & Partial<{ className: string | (() => string) }>
|
||||
) {
|
||||
let render = useRender()
|
||||
return (
|
||||
<div data-testid="wrapper-with-class">
|
||||
{render({
|
||||
@@ -97,6 +99,7 @@ describe('Default functionality', () => {
|
||||
}
|
||||
|
||||
function OtherDummy<TTag extends ElementType = 'div'>(props: Props<TTag>) {
|
||||
let render = useRender()
|
||||
return (
|
||||
<div data-testid="wrapper">
|
||||
{render({
|
||||
@@ -157,6 +160,7 @@ describe('Default functionality', () => {
|
||||
function Dummy<TTag extends ElementType = 'div'>(
|
||||
props: Props<TTag> & Partial<{ a: any; b: any; c: any }>
|
||||
) {
|
||||
let render = useRender()
|
||||
return (
|
||||
<div data-testid="wrapper">
|
||||
{render({
|
||||
@@ -179,6 +183,7 @@ describe('Default functionality', () => {
|
||||
function Dummy<TTag extends ElementType = 'div'>(
|
||||
props: Props<TTag> & Partial<{ a: any; b: any; c: any }>
|
||||
) {
|
||||
let render = useRender()
|
||||
return (
|
||||
<div data-testid="wrapper">
|
||||
{render({
|
||||
@@ -305,6 +310,7 @@ describe('Features.Static', () => {
|
||||
props: Expand<Props<TTag> & PropsForFeatures<typeof EnabledFeatures>> & { show: boolean }
|
||||
) {
|
||||
let { show, ...rest } = props
|
||||
let render = useRender()
|
||||
return (
|
||||
<div data-testid="wrapper">
|
||||
{render({
|
||||
@@ -380,6 +386,7 @@ describe('Features.RenderStrategy', () => {
|
||||
props: Expand<Props<TTag> & PropsForFeatures<typeof EnabledFeatures>> & { show: boolean }
|
||||
) {
|
||||
let { show, ...rest } = props
|
||||
let render = useRender()
|
||||
return (
|
||||
<div data-testid="wrapper">
|
||||
{render({
|
||||
@@ -408,6 +415,7 @@ describe('Features.Static | Features.RenderStrategy', () => {
|
||||
props: Expand<Props<TTag> & PropsForFeatures<typeof EnabledFeatures>> & { show: boolean }
|
||||
) {
|
||||
let { show, ...rest } = props
|
||||
let render = useRender()
|
||||
return (
|
||||
<div data-testid="wrapper">
|
||||
{render({
|
||||
|
||||
@@ -57,7 +57,16 @@ export type PropsForFeatures<T extends RenderFeatures> = Expand<
|
||||
>
|
||||
>
|
||||
|
||||
export function render<TFeature extends RenderFeatures, TTag extends ElementType, TSlot>({
|
||||
export function useRender() {
|
||||
let mergeRefs = useMergeRefsFn()
|
||||
|
||||
return useCallback(
|
||||
(args: Parameters<typeof render>[0]) => render({ mergeRefs, ...args }),
|
||||
[mergeRefs]
|
||||
) as typeof render
|
||||
}
|
||||
|
||||
function render<TFeature extends RenderFeatures, TTag extends ElementType, TSlot>({
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
@@ -77,7 +86,7 @@ export function render<TFeature extends RenderFeatures, TTag extends ElementType
|
||||
visible?: boolean
|
||||
name: string
|
||||
mergeRefs?: ReturnType<typeof useMergeRefsFn>
|
||||
}) {
|
||||
}): ReturnType<typeof _render> | null {
|
||||
mergeRefs = mergeRefs ?? defaultMergeRefs
|
||||
|
||||
let props = mergePropsAdvanced(theirProps, ourProps)
|
||||
@@ -281,7 +290,7 @@ function _render<TTag extends ElementType, TSlot>(
|
||||
* the `function` that updates these refs and can only do
|
||||
* so once the ref that contains the list is updated.
|
||||
*/
|
||||
export function useMergeRefsFn() {
|
||||
function useMergeRefsFn() {
|
||||
type MaybeRef<T> = MutableRefObject<T> | ((value: T) => void) | null | undefined
|
||||
let currentRefs = useRef<MaybeRef<any>[]>([])
|
||||
let mergedRef = useCallback((value: any) => {
|
||||
|
||||
Reference in New Issue
Block a user