Properly merge incoming props (#1265)
* rename inconsistent `passThroughProps` and `passthroughProps` to more
concise `incomingProps`
This is going to make a bit more sense in the next commits of this
branch, hold on!
* split props into `propsWeControl` and `propsTheyControl`
This will allow us to merge the props with a bit more control. Instead
of overriding every prop from the user' props with our props, we can now
merge event listeners.
* update `render` API to accept `propsWeControl` and `propsTheyControl`
* improve the merge logic
This will essentially do the exact same thing we were doing before:
```js
let props = { ...propsTheyControl, ...propsWeControl }
```
But instead of overriding everything, we will merge the event listener
related props like `onClick`, `onKeyDown`, ...
* fix typo in tests
* simplify naming
- Rename `propsWeControl` to `ourProps`
- Rename `propsTheyControl` to `theirProps`
* update changelog
This commit is contained in:
@@ -330,7 +330,7 @@ let ComboboxRoot = forwardRefWithAs(function Combobox<
|
||||
},
|
||||
ref: Ref<TTag>
|
||||
) {
|
||||
let { name, value, onChange, disabled = false, __demoMode = false, ...passThroughProps } = props
|
||||
let { name, value, onChange, disabled = false, __demoMode = false, ...theirProps } = props
|
||||
|
||||
let comboboxPropsRef = useRef<StateDefinition['comboboxPropsRef']['current']>({
|
||||
value,
|
||||
@@ -481,9 +481,11 @@ let ComboboxRoot = forwardRefWithAs(function Combobox<
|
||||
|
||||
// Ensure that we update the inputRef if the value changes
|
||||
useIsoMorphicEffect(syncInputValue, [syncInputValue])
|
||||
let ourProps = ref === null ? {} : { ref }
|
||||
|
||||
let renderConfiguration = {
|
||||
props: ref === null ? passThroughProps : { ...passThroughProps, ref },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_COMBOBOX_TAG,
|
||||
name: 'Combobox',
|
||||
@@ -556,7 +558,7 @@ let Input = forwardRefWithAs(function Input<
|
||||
},
|
||||
ref: Ref<HTMLInputElement>
|
||||
) {
|
||||
let { value, onChange, displayValue, ...passThroughProps } = props
|
||||
let { value, onChange, displayValue, ...theirProps } = props
|
||||
let [state] = useComboboxContext('Combobox.Input')
|
||||
let data = useComboboxData()
|
||||
let actions = useComboboxActions()
|
||||
@@ -677,7 +679,7 @@ let Input = forwardRefWithAs(function Input<
|
||||
[state]
|
||||
)
|
||||
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
ref: inputRef,
|
||||
id,
|
||||
role: 'combobox',
|
||||
@@ -694,7 +696,8 @@ let Input = forwardRefWithAs(function Input<
|
||||
}
|
||||
|
||||
return render({
|
||||
props: { ...passThroughProps, ...propsWeControl },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_INPUT_TAG,
|
||||
name: 'Combobox.Input',
|
||||
@@ -806,8 +809,8 @@ let Button = forwardRefWithAs(function Button<TTag extends ElementType = typeof
|
||||
() => ({ open: state.comboboxState === ComboboxStates.Open, disabled: state.disabled }),
|
||||
[state]
|
||||
)
|
||||
let passthroughProps = props
|
||||
let propsWeControl = {
|
||||
let theirProps = props
|
||||
let ourProps = {
|
||||
ref: buttonRef,
|
||||
id,
|
||||
type: useResolveButtonType(props, state.buttonRef),
|
||||
@@ -822,7 +825,8 @@ let Button = forwardRefWithAs(function Button<TTag extends ElementType = typeof
|
||||
}
|
||||
|
||||
return render({
|
||||
props: { ...passthroughProps, ...propsWeControl },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_BUTTON_TAG,
|
||||
name: 'Combobox.Button',
|
||||
@@ -855,9 +859,13 @@ let Label = forwardRefWithAs(function Label<TTag extends ElementType = typeof DE
|
||||
() => ({ open: state.comboboxState === ComboboxStates.Open, disabled: state.disabled }),
|
||||
[state]
|
||||
)
|
||||
let propsWeControl = { ref: labelRef, id, onClick: handleClick }
|
||||
|
||||
let theirProps = props
|
||||
let ourProps = { ref: labelRef, id, onClick: handleClick }
|
||||
|
||||
return render({
|
||||
props: { ...props, ...propsWeControl },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_LABEL_TAG,
|
||||
name: 'Combobox.Label',
|
||||
@@ -890,7 +898,7 @@ let Options = forwardRefWithAs(function Options<
|
||||
},
|
||||
ref: Ref<HTMLUListElement>
|
||||
) {
|
||||
let { hold = false, ...passthroughProps } = props
|
||||
let { hold = false, ...theirProps } = props
|
||||
let [state] = useComboboxContext('Combobox.Options')
|
||||
let { optionsPropsRef } = state
|
||||
|
||||
@@ -936,7 +944,7 @@ let Options = forwardRefWithAs(function Options<
|
||||
() => ({ open: state.comboboxState === ComboboxStates.Open }),
|
||||
[state]
|
||||
)
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
'aria-activedescendant':
|
||||
state.activeOptionIndex === null ? undefined : state.options[state.activeOptionIndex]?.id,
|
||||
'aria-labelledby': labelledby,
|
||||
@@ -946,7 +954,8 @@ let Options = forwardRefWithAs(function Options<
|
||||
}
|
||||
|
||||
return render({
|
||||
props: { ...passthroughProps, ...propsWeControl },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_OPTIONS_TAG,
|
||||
features: OptionsRenderFeatures,
|
||||
@@ -986,7 +995,7 @@ let Option = forwardRefWithAs(function Option<
|
||||
},
|
||||
ref: Ref<HTMLLIElement>
|
||||
) {
|
||||
let { disabled = false, value, ...passthroughProps } = props
|
||||
let { disabled = false, value, ...theirProps } = props
|
||||
let [state] = useComboboxContext('Combobox.Option')
|
||||
let data = useComboboxData()
|
||||
let actions = useComboboxActions()
|
||||
@@ -1072,7 +1081,7 @@ let Option = forwardRefWithAs(function Option<
|
||||
[active, selected, disabled]
|
||||
)
|
||||
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
id,
|
||||
ref: optionRef,
|
||||
role: 'option',
|
||||
@@ -1092,7 +1101,8 @@ let Option = forwardRefWithAs(function Option<
|
||||
}
|
||||
|
||||
return render({
|
||||
props: { ...passthroughProps, ...propsWeControl },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_OPTION_TAG,
|
||||
name: 'Combobox.Option',
|
||||
|
||||
@@ -98,11 +98,12 @@ export let Description = forwardRefWithAs(function Description<
|
||||
|
||||
useIsoMorphicEffect(() => context.register(id), [id, context.register])
|
||||
|
||||
let passThroughProps = props
|
||||
let propsWeControl = { ref: descriptionRef, ...context.props, id }
|
||||
let theirProps = props
|
||||
let ourProps = { ref: descriptionRef, ...context.props, id }
|
||||
|
||||
return render({
|
||||
props: { ...passThroughProps, ...propsWeControl },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot: context.slot || {},
|
||||
defaultTag: DEFAULT_DESCRIPTION_TAG,
|
||||
name: context.name || 'Description',
|
||||
|
||||
@@ -119,7 +119,7 @@ let DialogRoot = forwardRefWithAs(function Dialog<
|
||||
},
|
||||
ref: Ref<HTMLDivElement>
|
||||
) {
|
||||
let { open, onClose, initialFocus, __demoMode = false, ...rest } = props
|
||||
let { open, onClose, initialFocus, __demoMode = false, ...theirProps } = props
|
||||
let [nestedDialogCount, setNestedDialogCount] = useState(0)
|
||||
|
||||
let usesOpenClosedState = useOpenClosed()
|
||||
@@ -292,7 +292,7 @@ let DialogRoot = forwardRefWithAs(function Dialog<
|
||||
[dialogState]
|
||||
)
|
||||
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
ref: dialogRef,
|
||||
id,
|
||||
role: 'dialog',
|
||||
@@ -303,7 +303,6 @@ let DialogRoot = forwardRefWithAs(function Dialog<
|
||||
event.stopPropagation()
|
||||
},
|
||||
}
|
||||
let passthroughProps = rest
|
||||
|
||||
return (
|
||||
<StackProvider
|
||||
@@ -331,7 +330,8 @@ let DialogRoot = forwardRefWithAs(function Dialog<
|
||||
<ForcePortalRoot force={false}>
|
||||
<DescriptionProvider slot={slot} name="Dialog.Description">
|
||||
{render({
|
||||
props: { ...passthroughProps, ...propsWeControl },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_DIALOG_TAG,
|
||||
features: DialogRenderFeatures,
|
||||
@@ -379,16 +379,18 @@ let Overlay = forwardRefWithAs(function Overlay<
|
||||
() => ({ open: dialogState === DialogStates.Open }),
|
||||
[dialogState]
|
||||
)
|
||||
let propsWeControl = {
|
||||
|
||||
let theirProps = props
|
||||
let ourProps = {
|
||||
ref: overlayRef,
|
||||
id,
|
||||
'aria-hidden': true,
|
||||
onClick: handleClick,
|
||||
}
|
||||
let passthroughProps = props
|
||||
|
||||
return render({
|
||||
props: { ...passthroughProps, ...propsWeControl },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_OVERLAY_TAG,
|
||||
name: 'Dialog.Overlay',
|
||||
@@ -421,11 +423,13 @@ let Title = forwardRefWithAs(function Title<TTag extends ElementType = typeof DE
|
||||
() => ({ open: dialogState === DialogStates.Open }),
|
||||
[dialogState]
|
||||
)
|
||||
let propsWeControl = { id }
|
||||
let passthroughProps = props
|
||||
|
||||
let theirProps = props
|
||||
let ourProps = { ref: titleRef, id }
|
||||
|
||||
return render({
|
||||
props: { ref: titleRef, ...passthroughProps, ...propsWeControl },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_TITLE_TAG,
|
||||
name: 'Dialog.Title',
|
||||
|
||||
@@ -156,7 +156,7 @@ let DisclosureRoot = forwardRefWithAs(function Disclosure<
|
||||
},
|
||||
ref: Ref<TTag>
|
||||
) {
|
||||
let { defaultOpen = false, ...passthroughProps } = props
|
||||
let { defaultOpen = false, ...theirProps } = props
|
||||
let buttonId = `headlessui-disclosure-button-${useId()}`
|
||||
let panelId = `headlessui-disclosure-panel-${useId()}`
|
||||
let internalDisclosureRef = useRef<HTMLElement | null>(null)
|
||||
@@ -214,6 +214,10 @@ let DisclosureRoot = forwardRefWithAs(function Disclosure<
|
||||
[disclosureState, close]
|
||||
)
|
||||
|
||||
let ourProps = {
|
||||
ref: disclosureRef,
|
||||
}
|
||||
|
||||
return (
|
||||
<DisclosureContext.Provider value={reducerBag}>
|
||||
<DisclosureAPIContext.Provider value={api}>
|
||||
@@ -224,7 +228,8 @@ let DisclosureRoot = forwardRefWithAs(function Disclosure<
|
||||
})}
|
||||
>
|
||||
{render({
|
||||
props: { ref: disclosureRef, ...passthroughProps },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_DISCLOSURE_TAG,
|
||||
name: 'Disclosure',
|
||||
@@ -320,8 +325,8 @@ let Button = forwardRefWithAs(function Button<TTag extends ElementType = typeof
|
||||
)
|
||||
|
||||
let type = useResolveButtonType(props, internalButtonRef)
|
||||
let passthroughProps = props
|
||||
let propsWeControl = isWithinPanel
|
||||
let theirProps = props
|
||||
let ourProps = isWithinPanel
|
||||
? { ref: buttonRef, type, onKeyDown: handleKeyDown, onClick: handleClick }
|
||||
: {
|
||||
ref: buttonRef,
|
||||
@@ -337,7 +342,8 @@ let Button = forwardRefWithAs(function Button<TTag extends ElementType = typeof
|
||||
}
|
||||
|
||||
return render({
|
||||
props: { ...passthroughProps, ...propsWeControl },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_BUTTON_TAG,
|
||||
name: 'Disclosure.Button',
|
||||
@@ -391,16 +397,18 @@ let Panel = forwardRefWithAs(function Panel<TTag extends ElementType = typeof DE
|
||||
() => ({ open: state.disclosureState === DisclosureStates.Open, close }),
|
||||
[state, close]
|
||||
)
|
||||
let propsWeControl = {
|
||||
|
||||
let theirProps = props
|
||||
let ourProps = {
|
||||
ref: panelRef,
|
||||
id: state.panelId,
|
||||
}
|
||||
let passthroughProps = props
|
||||
|
||||
return (
|
||||
<DisclosurePanelContext.Provider value={state.panelId}>
|
||||
{render({
|
||||
props: { ...passthroughProps, ...propsWeControl },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_PANEL_TAG,
|
||||
features: PanelRenderFeatures,
|
||||
|
||||
@@ -23,17 +23,18 @@ export let FocusTrap = forwardRefWithAs(function FocusTrap<
|
||||
) {
|
||||
let container = useRef<HTMLElement | null>(null)
|
||||
let focusTrapRef = useSyncRefs(container, ref)
|
||||
let { initialFocus, ...passthroughProps } = props
|
||||
let { initialFocus, ...theirProps } = props
|
||||
|
||||
let ready = useServerHandoffComplete()
|
||||
useFocusTrap(container, ready ? FocusTrapFeatures.All : FocusTrapFeatures.None, { initialFocus })
|
||||
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
ref: focusTrapRef,
|
||||
}
|
||||
|
||||
return render({
|
||||
props: { ...passthroughProps, ...propsWeControl },
|
||||
ourProps,
|
||||
theirProps,
|
||||
defaultTag: DEFAULT_FOCUS_TRAP_TAG,
|
||||
name: 'FocusTrap',
|
||||
})
|
||||
|
||||
@@ -88,22 +88,28 @@ export let Label = forwardRefWithAs(function Label<
|
||||
},
|
||||
ref: Ref<HTMLLabelElement>
|
||||
) {
|
||||
let { passive = false, ...passThroughProps } = props
|
||||
let { passive = false, ...theirProps } = props
|
||||
let context = useLabelContext()
|
||||
let id = `headlessui-label-${useId()}`
|
||||
let labelRef = useSyncRefs(ref)
|
||||
|
||||
useIsoMorphicEffect(() => context.register(id), [id, context.register])
|
||||
|
||||
let propsWeControl = { ref: labelRef, ...context.props, id }
|
||||
let ourProps = { ref: labelRef, ...context.props, id }
|
||||
|
||||
let allProps = { ...passThroughProps, ...propsWeControl }
|
||||
// @ts-expect-error props are dynamic via context, some components will
|
||||
// provide an onClick then we can delete it.
|
||||
if (passive) delete allProps['onClick']
|
||||
if (passive) {
|
||||
if ('onClick' in ourProps) {
|
||||
delete (ourProps as any)['onClick']
|
||||
}
|
||||
|
||||
if ('onClick' in theirProps) {
|
||||
delete (theirProps as any)['onClick']
|
||||
}
|
||||
}
|
||||
|
||||
return render({
|
||||
props: allProps,
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot: context.slot || {},
|
||||
defaultTag: DEFAULT_LABEL_TAG,
|
||||
name: context.name || 'Label',
|
||||
|
||||
@@ -314,7 +314,7 @@ let ListboxRoot = forwardRefWithAs(function Listbox<
|
||||
},
|
||||
ref: Ref<TTag>
|
||||
) {
|
||||
let { value, name, onChange, disabled = false, horizontal = false, ...passThroughProps } = props
|
||||
let { value, name, onChange, disabled = false, horizontal = false, ...theirProps } = props
|
||||
const orientation = horizontal ? 'horizontal' : 'vertical'
|
||||
let listboxRef = useSyncRefs(ref)
|
||||
|
||||
@@ -382,8 +382,11 @@ let ListboxRoot = forwardRefWithAs(function Listbox<
|
||||
[listboxState, disabled]
|
||||
)
|
||||
|
||||
let ourProps = { ref: listboxRef }
|
||||
|
||||
let renderConfiguration = {
|
||||
props: { ref: listboxRef, ...passThroughProps },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_LISTBOX_TAG,
|
||||
name: 'Listbox',
|
||||
@@ -513,8 +516,8 @@ let Button = forwardRefWithAs(function Button<TTag extends ElementType = typeof
|
||||
() => ({ open: state.listboxState === ListboxStates.Open, disabled: state.disabled }),
|
||||
[state]
|
||||
)
|
||||
let passthroughProps = props
|
||||
let propsWeControl = {
|
||||
let theirProps = props
|
||||
let ourProps = {
|
||||
ref: buttonRef,
|
||||
id,
|
||||
type: useResolveButtonType(props, state.buttonRef),
|
||||
@@ -529,7 +532,8 @@ let Button = forwardRefWithAs(function Button<TTag extends ElementType = typeof
|
||||
}
|
||||
|
||||
return render({
|
||||
props: { ...passthroughProps, ...propsWeControl },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_BUTTON_TAG,
|
||||
name: 'Listbox.Button',
|
||||
@@ -562,9 +566,12 @@ let Label = forwardRefWithAs(function Label<TTag extends ElementType = typeof DE
|
||||
() => ({ open: state.listboxState === ListboxStates.Open, disabled: state.disabled }),
|
||||
[state]
|
||||
)
|
||||
let propsWeControl = { ref: labelRef, id, onClick: handleClick }
|
||||
let theirProps = props
|
||||
let ourProps = { ref: labelRef, id, onClick: handleClick }
|
||||
|
||||
return render({
|
||||
props: { ...props, ...propsWeControl },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_LABEL_TAG,
|
||||
name: 'Listbox.Label',
|
||||
@@ -702,7 +709,9 @@ let Options = forwardRefWithAs(function Options<
|
||||
() => ({ open: state.listboxState === ListboxStates.Open }),
|
||||
[state]
|
||||
)
|
||||
let propsWeControl = {
|
||||
|
||||
let theirProps = props
|
||||
let ourProps = {
|
||||
'aria-activedescendant':
|
||||
state.activeOptionIndex === null ? undefined : state.options[state.activeOptionIndex]?.id,
|
||||
'aria-multiselectable': state.propsRef.current.mode === ValueMode.Multi ? true : undefined,
|
||||
@@ -714,10 +723,10 @@ let Options = forwardRefWithAs(function Options<
|
||||
tabIndex: 0,
|
||||
ref: optionsRef,
|
||||
}
|
||||
let passthroughProps = props
|
||||
|
||||
return render({
|
||||
props: { ...passthroughProps, ...propsWeControl },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_OPTIONS_TAG,
|
||||
features: OptionsRenderFeatures,
|
||||
@@ -758,7 +767,7 @@ let Option = forwardRefWithAs(function Option<
|
||||
},
|
||||
ref: Ref<HTMLElement>
|
||||
) {
|
||||
let { disabled = false, value, ...passthroughProps } = props
|
||||
let { disabled = false, value, ...theirProps } = props
|
||||
let [state, dispatch] = useListboxContext('Listbox.Option')
|
||||
let id = `headlessui-listbox-option-${useId()}`
|
||||
let active =
|
||||
@@ -839,7 +848,7 @@ let Option = forwardRefWithAs(function Option<
|
||||
() => ({ active, selected, disabled }),
|
||||
[active, selected, disabled]
|
||||
)
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
id,
|
||||
ref: optionRef,
|
||||
role: 'option',
|
||||
@@ -859,7 +868,8 @@ let Option = forwardRefWithAs(function Option<
|
||||
}
|
||||
|
||||
return render({
|
||||
props: { ...passthroughProps, ...propsWeControl },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_OPTION_TAG,
|
||||
name: 'Listbox.Option',
|
||||
|
||||
@@ -1005,7 +1005,7 @@ describe('Keyboard interactions', () => {
|
||||
// Click the menu button again
|
||||
await click(getMenuButton())
|
||||
|
||||
// Active the last menu item
|
||||
// Activate the last menu item
|
||||
await mouseMove(getMenuItems()[2])
|
||||
|
||||
// Close menu, and invoke the item
|
||||
|
||||
@@ -255,6 +255,9 @@ let MenuRoot = forwardRefWithAs(function Menu<TTag extends ElementType = typeof
|
||||
[menuState]
|
||||
)
|
||||
|
||||
let theirProps = props
|
||||
let ourProps = { ref: menuRef }
|
||||
|
||||
return (
|
||||
<MenuContext.Provider value={reducerBag}>
|
||||
<OpenClosedProvider
|
||||
@@ -264,7 +267,8 @@ let MenuRoot = forwardRefWithAs(function Menu<TTag extends ElementType = typeof
|
||||
})}
|
||||
>
|
||||
{render({
|
||||
props: { ref: menuRef, ...props },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_MENU_TAG,
|
||||
name: 'Menu',
|
||||
@@ -355,8 +359,8 @@ let Button = forwardRefWithAs(function Button<TTag extends ElementType = typeof
|
||||
() => ({ open: state.menuState === MenuStates.Open }),
|
||||
[state]
|
||||
)
|
||||
let passthroughProps = props
|
||||
let propsWeControl = {
|
||||
let theirProps = props
|
||||
let ourProps = {
|
||||
ref: buttonRef,
|
||||
id,
|
||||
type: useResolveButtonType(props, state.buttonRef),
|
||||
@@ -369,7 +373,8 @@ let Button = forwardRefWithAs(function Button<TTag extends ElementType = typeof
|
||||
}
|
||||
|
||||
return render({
|
||||
props: { ...passthroughProps, ...propsWeControl },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_BUTTON_TAG,
|
||||
name: 'Menu.Button',
|
||||
@@ -521,7 +526,9 @@ let Items = forwardRefWithAs(function Items<TTag extends ElementType = typeof DE
|
||||
() => ({ open: state.menuState === MenuStates.Open }),
|
||||
[state]
|
||||
)
|
||||
let propsWeControl = {
|
||||
|
||||
let theirProps = props
|
||||
let ourProps = {
|
||||
'aria-activedescendant':
|
||||
state.activeItemIndex === null ? undefined : state.items[state.activeItemIndex]?.id,
|
||||
'aria-labelledby': state.buttonRef.current?.id,
|
||||
@@ -532,10 +539,10 @@ let Items = forwardRefWithAs(function Items<TTag extends ElementType = typeof DE
|
||||
tabIndex: 0,
|
||||
ref: itemsRef,
|
||||
}
|
||||
let passthroughProps = props
|
||||
|
||||
return render({
|
||||
props: { ...passthroughProps, ...propsWeControl },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_ITEMS_TAG,
|
||||
features: ItemsRenderFeatures,
|
||||
@@ -565,11 +572,10 @@ type MenuItemPropsWeControl =
|
||||
let Item = forwardRefWithAs(function Item<TTag extends ElementType = typeof DEFAULT_ITEM_TAG>(
|
||||
props: Props<TTag, ItemRenderPropArg, MenuItemPropsWeControl> & {
|
||||
disabled?: boolean
|
||||
onClick?: (event: { preventDefault: Function }) => void
|
||||
},
|
||||
ref: Ref<HTMLElement>
|
||||
) {
|
||||
let { disabled = false, onClick, ...passthroughProps } = props
|
||||
let { disabled = false, ...theirProps } = props
|
||||
let [state, dispatch] = useMenuContext('Menu.Item')
|
||||
let id = `headlessui-menu-item-${useId()}`
|
||||
let active = state.activeItemIndex !== null ? state.items[state.activeItemIndex].id === id : false
|
||||
@@ -606,9 +612,8 @@ let Item = forwardRefWithAs(function Item<TTag extends ElementType = typeof DEFA
|
||||
if (disabled) return event.preventDefault()
|
||||
dispatch({ type: ActionTypes.CloseMenu })
|
||||
disposables().nextFrame(() => state.buttonRef.current?.focus({ preventScroll: true }))
|
||||
if (onClick) return onClick(event)
|
||||
},
|
||||
[dispatch, state.buttonRef, disabled, onClick]
|
||||
[dispatch, state.buttonRef, disabled]
|
||||
)
|
||||
|
||||
let handleFocus = useCallback(() => {
|
||||
@@ -634,7 +639,7 @@ let Item = forwardRefWithAs(function Item<TTag extends ElementType = typeof DEFA
|
||||
}, [disabled, active, dispatch])
|
||||
|
||||
let slot = useMemo<ItemRenderPropArg>(() => ({ active, disabled }), [active, disabled])
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
id,
|
||||
ref: itemRef,
|
||||
role: 'menuitem',
|
||||
@@ -650,7 +655,8 @@ let Item = forwardRefWithAs(function Item<TTag extends ElementType = typeof DEFA
|
||||
}
|
||||
|
||||
return render({
|
||||
props: { ...passthroughProps, ...propsWeControl },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_ITEM_TAG,
|
||||
name: 'Menu.Item',
|
||||
|
||||
@@ -261,6 +261,9 @@ let PopoverRoot = forwardRefWithAs(function Popover<
|
||||
[popoverState, close]
|
||||
)
|
||||
|
||||
let theirProps = props
|
||||
let ourProps = { ref: popoverRef }
|
||||
|
||||
return (
|
||||
<PopoverContext.Provider value={reducerBag}>
|
||||
<PopoverAPIContext.Provider value={api}>
|
||||
@@ -271,7 +274,8 @@ let PopoverRoot = forwardRefWithAs(function Popover<
|
||||
})}
|
||||
>
|
||||
{render({
|
||||
props: { ref: popoverRef, ...props },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_POPOVER_TAG,
|
||||
name: 'Popover',
|
||||
@@ -485,8 +489,8 @@ let Button = forwardRefWithAs(function Button<TTag extends ElementType = typeof
|
||||
)
|
||||
|
||||
let type = useResolveButtonType(props, internalButtonRef)
|
||||
let passthroughProps = props
|
||||
let propsWeControl = isWithinPanel
|
||||
let theirProps = props
|
||||
let ourProps = isWithinPanel
|
||||
? {
|
||||
ref: withinPanelButtonRef,
|
||||
type,
|
||||
@@ -505,7 +509,8 @@ let Button = forwardRefWithAs(function Button<TTag extends ElementType = typeof
|
||||
}
|
||||
|
||||
return render({
|
||||
props: { ...passthroughProps, ...propsWeControl },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_BUTTON_TAG,
|
||||
name: 'Popover.Button',
|
||||
@@ -555,16 +560,18 @@ let Overlay = forwardRefWithAs(function Overlay<
|
||||
() => ({ open: popoverState === PopoverStates.Open }),
|
||||
[popoverState]
|
||||
)
|
||||
let propsWeControl = {
|
||||
|
||||
let theirProps = props
|
||||
let ourProps = {
|
||||
ref: overlayRef,
|
||||
id,
|
||||
'aria-hidden': true,
|
||||
onClick: handleClick,
|
||||
}
|
||||
let passthroughProps = props
|
||||
|
||||
return render({
|
||||
props: { ...passthroughProps, ...propsWeControl },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_OVERLAY_TAG,
|
||||
features: OverlayRenderFeatures,
|
||||
@@ -591,7 +598,7 @@ let Panel = forwardRefWithAs(function Panel<TTag extends ElementType = typeof DE
|
||||
},
|
||||
ref: Ref<HTMLDivElement>
|
||||
) {
|
||||
let { focus = false, ...passthroughProps } = props
|
||||
let { focus = false, ...theirProps } = props
|
||||
|
||||
let [state, dispatch] = usePopoverContext('Popover.Panel')
|
||||
let { close } = usePopoverAPIContext('Popover.Panel')
|
||||
@@ -721,7 +728,7 @@ let Panel = forwardRefWithAs(function Panel<TTag extends ElementType = typeof DE
|
||||
() => ({ open: state.popoverState === PopoverStates.Open, close }),
|
||||
[state, close]
|
||||
)
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
ref: panelRef,
|
||||
id: state.panelId,
|
||||
onKeyDown: handleKeyDown,
|
||||
@@ -730,7 +737,8 @@ let Panel = forwardRefWithAs(function Panel<TTag extends ElementType = typeof DE
|
||||
return (
|
||||
<PopoverPanelContext.Provider value={state.panelId}>
|
||||
{render({
|
||||
props: { ...passthroughProps, ...propsWeControl },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_PANEL_TAG,
|
||||
features: PanelRenderFeatures,
|
||||
@@ -814,13 +822,15 @@ let Group = forwardRefWithAs(function Group<TTag extends ElementType = typeof DE
|
||||
)
|
||||
|
||||
let slot = useMemo<GroupRenderPropArg>(() => ({}), [])
|
||||
let propsWeControl = { ref: groupRef }
|
||||
let passthroughProps = props
|
||||
|
||||
let theirProps = props
|
||||
let ourProps = { ref: groupRef }
|
||||
|
||||
return (
|
||||
<PopoverGroupContext.Provider value={contextBag}>
|
||||
{render({
|
||||
props: { ...passthroughProps, ...propsWeControl },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_GROUP_TAG,
|
||||
name: 'Popover.Group',
|
||||
|
||||
@@ -69,7 +69,7 @@ interface PortalRenderPropArg {}
|
||||
let PortalRoot = forwardRefWithAs(function Portal<
|
||||
TTag extends ElementType = typeof DEFAULT_PORTAL_TAG
|
||||
>(props: Props<TTag, PortalRenderPropArg>, ref: Ref<HTMLElement>) {
|
||||
let passthroughProps = props
|
||||
let theirProps = props
|
||||
let internalPortalRootRef = useRef<HTMLElement | null>(null)
|
||||
let portalRef = useSyncRefs(
|
||||
optionalRef<typeof internalPortalRootRef['current']>((ref) => {
|
||||
@@ -105,11 +105,14 @@ let PortalRoot = forwardRefWithAs(function Portal<
|
||||
|
||||
if (!ready) return null
|
||||
|
||||
let ourProps = { ref: portalRef }
|
||||
|
||||
return !target || !element
|
||||
? null
|
||||
: createPortal(
|
||||
render({
|
||||
props: { ref: portalRef, ...passthroughProps },
|
||||
ourProps,
|
||||
theirProps,
|
||||
defaultTag: DEFAULT_PORTAL_TAG,
|
||||
name: 'Portal',
|
||||
}),
|
||||
@@ -130,13 +133,16 @@ let Group = forwardRefWithAs(function Group<TTag extends ElementType = typeof DE
|
||||
},
|
||||
ref: Ref<HTMLElement>
|
||||
) {
|
||||
let { target, ...passthroughProps } = props
|
||||
let { target, ...theirProps } = props
|
||||
let groupRef = useSyncRefs(ref)
|
||||
|
||||
let ourProps = { ref: groupRef }
|
||||
|
||||
return (
|
||||
<PortalGroupContext.Provider value={target}>
|
||||
{render({
|
||||
props: { ref: groupRef, ...passthroughProps },
|
||||
ourProps,
|
||||
theirProps,
|
||||
defaultTag: DEFAULT_GROUP_TAG,
|
||||
name: 'Popover.Group',
|
||||
})}
|
||||
|
||||
@@ -121,7 +121,7 @@ let RadioGroupRoot = forwardRefWithAs(function RadioGroup<
|
||||
},
|
||||
ref: Ref<HTMLElement>
|
||||
) {
|
||||
let { value, name, onChange, disabled = false, ...passThroughProps } = props
|
||||
let { value, name, onChange, disabled = false, ...theirProps } = props
|
||||
let [{ options }, dispatch] = useReducer(stateReducer, {
|
||||
options: [],
|
||||
} as StateDefinition)
|
||||
@@ -252,7 +252,7 @@ let RadioGroupRoot = forwardRefWithAs(function RadioGroup<
|
||||
[registerOption, firstOption, containsCheckedOption, triggerChange, disabled, value]
|
||||
)
|
||||
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
ref: radioGroupRef,
|
||||
id,
|
||||
role: 'radiogroup',
|
||||
@@ -262,7 +262,8 @@ let RadioGroupRoot = forwardRefWithAs(function RadioGroup<
|
||||
}
|
||||
|
||||
let renderConfiguration = {
|
||||
props: { ...passThroughProps, ...propsWeControl },
|
||||
ourProps,
|
||||
theirProps,
|
||||
defaultTag: DEFAULT_RADIO_GROUP_TAG,
|
||||
name: 'RadioGroup',
|
||||
}
|
||||
@@ -341,7 +342,7 @@ let Option = forwardRefWithAs(function Option<
|
||||
let [describedby, DescriptionProvider] = useDescriptions()
|
||||
let { addFlag, removeFlag, hasFlag } = useFlags(OptionState.Empty)
|
||||
|
||||
let { value, disabled = false, ...passThroughProps } = props
|
||||
let { value, disabled = false, ...theirProps } = props
|
||||
let propsRef = useRef({ value, disabled })
|
||||
|
||||
useIsoMorphicEffect(() => {
|
||||
@@ -379,7 +380,7 @@ let Option = forwardRefWithAs(function Option<
|
||||
let isDisabled = radioGroupDisabled || disabled
|
||||
|
||||
let checked = radioGroupValue === value
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
ref: optionRef,
|
||||
id,
|
||||
role: 'radio',
|
||||
@@ -406,7 +407,8 @@ let Option = forwardRefWithAs(function Option<
|
||||
<DescriptionProvider name="RadioGroup.Description">
|
||||
<LabelProvider name="RadioGroup.Label">
|
||||
{render({
|
||||
props: { ...passThroughProps, ...propsWeControl },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_OPTION_TAG,
|
||||
name: 'RadioGroup.Option',
|
||||
|
||||
@@ -49,6 +49,9 @@ function Group<TTag extends ElementType = typeof DEFAULT_GROUP_TAG>(props: Props
|
||||
[switchElement, setSwitchElement, labelledby, describedby]
|
||||
)
|
||||
|
||||
let ourProps = {}
|
||||
let theirProps = props
|
||||
|
||||
return (
|
||||
<DescriptionProvider name="Switch.Description">
|
||||
<LabelProvider
|
||||
@@ -62,7 +65,12 @@ function Group<TTag extends ElementType = typeof DEFAULT_GROUP_TAG>(props: Props
|
||||
}}
|
||||
>
|
||||
<GroupContext.Provider value={context}>
|
||||
{render({ props, defaultTag: DEFAULT_GROUP_TAG, name: 'Switch.Group' })}
|
||||
{render({
|
||||
ourProps,
|
||||
theirProps,
|
||||
defaultTag: DEFAULT_GROUP_TAG,
|
||||
name: 'Switch.Group',
|
||||
})}
|
||||
</GroupContext.Provider>
|
||||
</LabelProvider>
|
||||
</DescriptionProvider>
|
||||
@@ -101,7 +109,7 @@ let SwitchRoot = forwardRefWithAs(function Switch<
|
||||
},
|
||||
ref: Ref<HTMLElement>
|
||||
) {
|
||||
let { checked, onChange, name, value, ...passThroughProps } = props
|
||||
let { checked, onChange, name, value, ...theirProps } = props
|
||||
let id = `headlessui-switch-${useId()}`
|
||||
let groupContext = useContext(GroupContext)
|
||||
let internalSwitchRef = useRef<HTMLButtonElement | null>(null)
|
||||
@@ -136,7 +144,7 @@ let SwitchRoot = forwardRefWithAs(function Switch<
|
||||
)
|
||||
|
||||
let slot = useMemo<SwitchRenderPropArg>(() => ({ checked }), [checked])
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
id,
|
||||
ref: switchRef,
|
||||
role: 'switch',
|
||||
@@ -151,7 +159,8 @@ let SwitchRoot = forwardRefWithAs(function Switch<
|
||||
}
|
||||
|
||||
let renderConfiguration = {
|
||||
props: { ...passThroughProps, ...propsWeControl },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_SWITCH_TAG,
|
||||
name: 'Switch',
|
||||
|
||||
@@ -146,7 +146,7 @@ let Tabs = forwardRefWithAs(function Tabs<TTag extends ElementType = typeof DEFA
|
||||
manual = false,
|
||||
onChange,
|
||||
selectedIndex = null,
|
||||
...passThroughProps
|
||||
...theirProps
|
||||
} = props
|
||||
const orientation = vertical ? 'vertical' : 'horizontal'
|
||||
const activation = manual ? 'manual' : 'auto'
|
||||
@@ -228,6 +228,10 @@ let Tabs = forwardRefWithAs(function Tabs<TTag extends ElementType = typeof DEFA
|
||||
|
||||
let SSRCounter = useRef(0)
|
||||
|
||||
let ourProps = {
|
||||
ref: tabsRef,
|
||||
}
|
||||
|
||||
return (
|
||||
<TabsSSRContext.Provider value={typeof window === 'undefined' ? SSRCounter : null}>
|
||||
<TabsContext.Provider value={providerBag}>
|
||||
@@ -244,7 +248,8 @@ let Tabs = forwardRefWithAs(function Tabs<TTag extends ElementType = typeof DEFA
|
||||
}}
|
||||
/>
|
||||
{render({
|
||||
props: { ref: tabsRef, ...passThroughProps },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_TABS_TAG,
|
||||
name: 'Tabs',
|
||||
@@ -270,15 +275,17 @@ let List = forwardRefWithAs(function List<TTag extends ElementType = typeof DEFA
|
||||
let listRef = useSyncRefs(ref)
|
||||
|
||||
let slot = { selectedIndex }
|
||||
let propsWeControl = {
|
||||
|
||||
let theirProps = props
|
||||
let ourProps = {
|
||||
ref: listRef,
|
||||
role: 'tablist',
|
||||
'aria-orientation': orientation,
|
||||
}
|
||||
let passThroughProps = props
|
||||
|
||||
return render({
|
||||
props: { ...passThroughProps, ...propsWeControl },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_LIST_TAG,
|
||||
name: 'Tabs.List',
|
||||
@@ -377,7 +384,9 @@ let TabRoot = forwardRefWithAs(function Tab<TTag extends ElementType = typeof DE
|
||||
}, [])
|
||||
|
||||
let slot = useMemo(() => ({ selected }), [selected])
|
||||
let propsWeControl = {
|
||||
|
||||
let theirProps = props
|
||||
let ourProps = {
|
||||
ref: tabRef,
|
||||
onKeyDown: handleKeyDown,
|
||||
onFocus: activation === 'manual' ? handleFocus : handleSelection,
|
||||
@@ -390,10 +399,10 @@ let TabRoot = forwardRefWithAs(function Tab<TTag extends ElementType = typeof DE
|
||||
'aria-selected': selected,
|
||||
tabIndex: selected ? 0 : -1,
|
||||
}
|
||||
let passThroughProps = props
|
||||
|
||||
return render({
|
||||
props: { ...passThroughProps, ...propsWeControl },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_TAB_TAG,
|
||||
name: 'Tabs.Tab',
|
||||
@@ -416,8 +425,12 @@ let Panels = forwardRefWithAs(function Panels<TTag extends ElementType = typeof
|
||||
|
||||
let slot = useMemo(() => ({ selectedIndex }), [selectedIndex])
|
||||
|
||||
let theirProps = props
|
||||
let ourProps = { ref: panelsRef }
|
||||
|
||||
return render({
|
||||
props: { ref: panelsRef, ...props },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_PANELS_TAG,
|
||||
name: 'Tabs.Panels',
|
||||
@@ -462,7 +475,9 @@ let Panel = forwardRefWithAs(function Panel<TTag extends ElementType = typeof DE
|
||||
SSRContext === null ? myIndex === selectedIndex : SSRContext.current++ === selectedIndex
|
||||
|
||||
let slot = useMemo(() => ({ selected }), [selected])
|
||||
let propsWeControl = {
|
||||
|
||||
let theirProps = props
|
||||
let ourProps = {
|
||||
ref: panelRef,
|
||||
id,
|
||||
role: 'tabpanel',
|
||||
@@ -470,10 +485,9 @@ let Panel = forwardRefWithAs(function Panel<TTag extends ElementType = typeof DE
|
||||
tabIndex: selected ? 0 : -1,
|
||||
}
|
||||
|
||||
let passThroughProps = props
|
||||
|
||||
return render({
|
||||
props: { ...passThroughProps, ...propsWeControl },
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag: DEFAULT_PANEL_TAG,
|
||||
features: PanelRenderFeatures,
|
||||
|
||||
@@ -348,8 +348,8 @@ let TransitionChild = forwardRefWithAs(function TransitionChild<
|
||||
}
|
||||
}, [show, skip, state])
|
||||
|
||||
let propsWeControl = { ref: transitionRef }
|
||||
let passthroughProps = rest
|
||||
let theirProps = rest
|
||||
let ourProps = { ref: transitionRef }
|
||||
|
||||
return (
|
||||
<NestingContext.Provider value={nesting}>
|
||||
@@ -360,7 +360,8 @@ let TransitionChild = forwardRefWithAs(function TransitionChild<
|
||||
})}
|
||||
>
|
||||
{render({
|
||||
props: { ...passthroughProps, ...propsWeControl },
|
||||
ourProps,
|
||||
theirProps,
|
||||
defaultTag: DEFAULT_TRANSITION_CHILD_TAG,
|
||||
features: TransitionChildRenderFeatures,
|
||||
visible: state === TreeStates.Visible,
|
||||
@@ -375,7 +376,7 @@ let TransitionRoot = forwardRefWithAs(function Transition<
|
||||
TTag extends ElementType = typeof DEFAULT_TRANSITION_CHILD_TAG
|
||||
>(props: TransitionChildProps<TTag> & { show?: boolean; appear?: boolean }, ref: Ref<HTMLElement>) {
|
||||
// @ts-expect-error
|
||||
let { show, appear = false, unmount, ...passthroughProps } = props as typeof props
|
||||
let { show, appear = false, unmount, ...theirProps } = props as typeof props
|
||||
let transitionRef = useSyncRefs(ref)
|
||||
|
||||
let usesOpenClosedState = useOpenClosed()
|
||||
@@ -417,13 +418,12 @@ let TransitionRoot = forwardRefWithAs(function Transition<
|
||||
<NestingContext.Provider value={nestingBag}>
|
||||
<TransitionContext.Provider value={transitionBag}>
|
||||
{render({
|
||||
props: {
|
||||
ourProps: {
|
||||
...sharedProps,
|
||||
as: Fragment,
|
||||
children: (
|
||||
<TransitionChild ref={transitionRef} {...sharedProps} {...passthroughProps} />
|
||||
),
|
||||
children: <TransitionChild ref={transitionRef} {...sharedProps} {...theirProps} />,
|
||||
},
|
||||
theirProps: {},
|
||||
defaultTag: Fragment,
|
||||
features: TransitionChildRenderFeatures,
|
||||
visible: state === TreeStates.Visible,
|
||||
|
||||
Reference in New Issue
Block a user