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:
@@ -31,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Fix Tree-shaking support ([#1247](https://github.com/tailwindlabs/headlessui/pull/1247))
|
||||
- Stop propagation on the Popover Button ([#1263](https://github.com/tailwindlabs/headlessui/pull/1263))
|
||||
- Fix incorrect `active` option in the Listbox/Combobox component ([#1264](https://github.com/tailwindlabs/headlessui/pull/1264))
|
||||
- Properly merge incoming props ([#1265](https://github.com/tailwindlabs/headlessui/pull/1265))
|
||||
|
||||
### Added
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -7,22 +7,25 @@ let DEFAULT_VISUALLY_HIDDEN_TAG = 'div' as const
|
||||
export let VisuallyHidden = forwardRefWithAs(function VisuallyHidden<
|
||||
TTag extends ElementType = typeof DEFAULT_VISUALLY_HIDDEN_TAG
|
||||
>(props: Props<TTag>, ref: Ref<HTMLElement>) {
|
||||
return render({
|
||||
props: {
|
||||
...props,
|
||||
ref,
|
||||
style: {
|
||||
position: 'absolute',
|
||||
width: 1,
|
||||
height: 1,
|
||||
padding: 0,
|
||||
margin: -1,
|
||||
overflow: 'hidden',
|
||||
clip: 'rect(0, 0, 0, 0)',
|
||||
whiteSpace: 'nowrap',
|
||||
borderWidth: '0',
|
||||
},
|
||||
let theirProps = props
|
||||
let ourProps = {
|
||||
ref,
|
||||
style: {
|
||||
position: 'absolute',
|
||||
width: 1,
|
||||
height: 1,
|
||||
padding: 0,
|
||||
margin: -1,
|
||||
overflow: 'hidden',
|
||||
clip: 'rect(0, 0, 0, 0)',
|
||||
whiteSpace: 'nowrap',
|
||||
borderWidth: '0',
|
||||
},
|
||||
}
|
||||
|
||||
return render({
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot: {},
|
||||
defaultTag: DEFAULT_VISUALLY_HIDDEN_TAG,
|
||||
name: 'VisuallyHidden',
|
||||
|
||||
@@ -19,7 +19,8 @@ describe('Default functionality', () => {
|
||||
return (
|
||||
<div data-testid="wrapper">
|
||||
{render({
|
||||
props,
|
||||
ourProps: {},
|
||||
theirProps: props,
|
||||
slot,
|
||||
defaultTag: 'div',
|
||||
name: 'Dummy',
|
||||
@@ -80,7 +81,8 @@ describe('Default functionality', () => {
|
||||
return (
|
||||
<div data-testid="wrapper">
|
||||
{render({
|
||||
props: { ...props, ref },
|
||||
ourProps: { ref },
|
||||
theirProps: props,
|
||||
slot,
|
||||
defaultTag: 'div',
|
||||
name: 'OtherDummy',
|
||||
@@ -323,7 +325,8 @@ describe('Features.Static', () => {
|
||||
return (
|
||||
<div data-testid="wrapper">
|
||||
{render({
|
||||
props: rest,
|
||||
ourProps: {},
|
||||
theirProps: rest,
|
||||
slot,
|
||||
defaultTag: 'div',
|
||||
features: EnabledFeatures,
|
||||
@@ -428,7 +431,8 @@ describe('Features.RenderStrategy', () => {
|
||||
return (
|
||||
<div data-testid="wrapper">
|
||||
{render({
|
||||
props: rest,
|
||||
ourProps: {},
|
||||
theirProps: rest,
|
||||
slot,
|
||||
defaultTag: 'div',
|
||||
features: EnabledFeatures,
|
||||
@@ -455,7 +459,8 @@ describe('Features.Static | Features.RenderStrategy', () => {
|
||||
return (
|
||||
<div data-testid="wrapper">
|
||||
{render({
|
||||
props: rest,
|
||||
ourProps: {},
|
||||
theirProps: rest,
|
||||
slot,
|
||||
defaultTag: 'div',
|
||||
features: EnabledFeatures,
|
||||
|
||||
@@ -47,20 +47,24 @@ export type PropsForFeatures<T extends Features> = XOR<
|
||||
>
|
||||
|
||||
export function render<TFeature extends Features, TTag extends ElementType, TSlot>({
|
||||
props,
|
||||
ourProps,
|
||||
theirProps,
|
||||
slot,
|
||||
defaultTag,
|
||||
features,
|
||||
visible = true,
|
||||
name,
|
||||
}: {
|
||||
props: Expand<Props<TTag, TSlot, any> & PropsForFeatures<TFeature>>
|
||||
ourProps: Expand<Props<TTag, TSlot, any> & PropsForFeatures<TFeature>>
|
||||
theirProps: Expand<Props<TTag, TSlot, any>>
|
||||
slot?: TSlot
|
||||
defaultTag: ElementType
|
||||
features?: TFeature
|
||||
visible?: boolean
|
||||
name: string
|
||||
}) {
|
||||
let props = mergeProps(theirProps, ourProps)
|
||||
|
||||
// Visible always render
|
||||
if (visible) return _render(props, slot, defaultTag, name)
|
||||
|
||||
@@ -106,7 +110,7 @@ function _render<TTag extends ElementType, TSlot>(
|
||||
as: Component = tag,
|
||||
children,
|
||||
refName = 'ref',
|
||||
...passThroughProps
|
||||
...rest
|
||||
} = omit(props, ['unmount', 'static'])
|
||||
|
||||
// This allows us to use `<HeadlessUIComponent as={MyComponent} refName="innerRef" />`
|
||||
@@ -117,12 +121,12 @@ function _render<TTag extends ElementType, TSlot>(
|
||||
| ReactElement[]
|
||||
|
||||
// Allow for className to be a function with the slot as the contents
|
||||
if (passThroughProps.className && typeof passThroughProps.className === 'function') {
|
||||
;(passThroughProps as any).className = passThroughProps.className(slot)
|
||||
if (rest.className && typeof rest.className === 'function') {
|
||||
;(rest as any).className = rest.className(slot)
|
||||
}
|
||||
|
||||
if (Component === Fragment) {
|
||||
if (Object.keys(compact(passThroughProps)).length > 0) {
|
||||
if (Object.keys(compact(rest)).length > 0) {
|
||||
if (
|
||||
!isValidElement(resolvedChildren) ||
|
||||
(Array.isArray(resolvedChildren) && resolvedChildren.length > 1)
|
||||
@@ -133,7 +137,7 @@ function _render<TTag extends ElementType, TSlot>(
|
||||
'',
|
||||
`The current component <${name} /> is rendering a "Fragment".`,
|
||||
`However we need to passthrough the following props:`,
|
||||
Object.keys(passThroughProps)
|
||||
Object.keys(rest)
|
||||
.map((line) => ` - ${line}`)
|
||||
.join('\n'),
|
||||
'',
|
||||
@@ -153,9 +157,7 @@ function _render<TTag extends ElementType, TSlot>(
|
||||
Object.assign(
|
||||
{},
|
||||
// Filter out undefined values so that they don't override the existing values
|
||||
mergeEventFunctions(compact(omit(passThroughProps, ['ref'])), resolvedChildren.props, [
|
||||
'onClick',
|
||||
]),
|
||||
mergeProps(resolvedChildren.props, compact(omit(rest, ['ref']))),
|
||||
refRelatedProps
|
||||
)
|
||||
)
|
||||
@@ -164,46 +166,63 @@ function _render<TTag extends ElementType, TSlot>(
|
||||
|
||||
return createElement(
|
||||
Component,
|
||||
Object.assign({}, omit(passThroughProps, ['ref']), Component !== Fragment && refRelatedProps),
|
||||
Object.assign({}, omit(rest, ['ref']), Component !== Fragment && refRelatedProps),
|
||||
resolvedChildren
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* We can use this function for the following useCase:
|
||||
*
|
||||
* <Menu.Item> <button onClick={console.log} /> </Menu.Item>
|
||||
*
|
||||
* Our `Menu.Item` will have an internal `onClick`, if you passthrough an `onClick` to the actual
|
||||
* `Menu.Item` component we will call it correctly. However, when we have an `onClick` on the actual
|
||||
* first child, that one should _also_ be called (but before this implementation, it was just
|
||||
* overriding the `onClick`). But it is only when we *render* that we have access to the existing
|
||||
* props of this component.
|
||||
*
|
||||
* It's a bit hacky, and not that clean, but it is something internal and we have tests to rely on
|
||||
* so that we can refactor this later (if needed).
|
||||
*/
|
||||
function mergeEventFunctions(
|
||||
passThroughProps: Record<string, any>,
|
||||
existingProps: Record<string, any>,
|
||||
functionsToMerge: string[]
|
||||
) {
|
||||
let clone = Object.assign({}, passThroughProps)
|
||||
for (let func of functionsToMerge) {
|
||||
if (passThroughProps[func] !== undefined && existingProps[func] !== undefined) {
|
||||
Object.assign(clone, {
|
||||
[func](event: { defaultPrevented: boolean }) {
|
||||
// Props we control
|
||||
if (!event.defaultPrevented) passThroughProps[func](event)
|
||||
function mergeProps(...listOfProps: Props<any, any>[]) {
|
||||
if (listOfProps.length === 0) return {}
|
||||
if (listOfProps.length === 1) return listOfProps[0]
|
||||
|
||||
// Existing props on the component
|
||||
if (!event.defaultPrevented) existingProps[func](event)
|
||||
},
|
||||
})
|
||||
let target: Props<any, any> = {}
|
||||
|
||||
let eventHandlers: Record<
|
||||
string,
|
||||
((event: { defaultPrevented: boolean }) => void | undefined)[]
|
||||
> = {}
|
||||
|
||||
for (let props of listOfProps) {
|
||||
for (let prop in props) {
|
||||
// Collect event handlers
|
||||
if (prop.startsWith('on') && typeof props[prop] === 'function') {
|
||||
eventHandlers[prop] ??= []
|
||||
eventHandlers[prop].push(props[prop])
|
||||
} else {
|
||||
// Override incoming prop
|
||||
target[prop] = props[prop]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return clone
|
||||
// Do not attach any event handlers when there is a `disabled` or `aria-disabled` prop set.
|
||||
if (target.disabled || target['aria-disabled']) {
|
||||
return Object.assign(
|
||||
target,
|
||||
// Set all event listeners that we collected to `undefined`. This is
|
||||
// important because of the `cloneElement` from above, which merges the
|
||||
// existing and new props, they don't just override therefore we have to
|
||||
// explicitly nullify them.
|
||||
Object.fromEntries(Object.keys(eventHandlers).map((eventName) => [eventName, undefined]))
|
||||
)
|
||||
}
|
||||
|
||||
// Merge event handlers
|
||||
for (let eventName in eventHandlers) {
|
||||
Object.assign(target, {
|
||||
[eventName](event: { defaultPrevented: boolean }) {
|
||||
let handlers = eventHandlers[eventName]
|
||||
|
||||
for (let handler of handlers) {
|
||||
if (event.defaultPrevented) return
|
||||
|
||||
handler(event)
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return target
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -342,7 +342,7 @@ export let Combobox = defineComponent({
|
||||
)
|
||||
|
||||
return () => {
|
||||
let { name, modelValue, disabled, ...passThroughProps } = props
|
||||
let { name, modelValue, disabled, ...incomingProps } = props
|
||||
let slot = {
|
||||
open: comboboxState.value === ComboboxStates.Open,
|
||||
disabled,
|
||||
@@ -351,7 +351,7 @@ export let Combobox = defineComponent({
|
||||
}
|
||||
|
||||
let renderConfiguration = {
|
||||
props: omit(passThroughProps, ['onUpdate:modelValue']),
|
||||
props: omit(incomingProps, ['onUpdate:modelValue']),
|
||||
slot,
|
||||
slots,
|
||||
attrs,
|
||||
@@ -402,10 +402,10 @@ export let ComboboxLabel = defineComponent({
|
||||
disabled: api.disabled.value,
|
||||
}
|
||||
|
||||
let propsWeControl = { id, ref: api.labelRef, onClick: handleClick }
|
||||
let ourProps = { id, ref: api.labelRef, onClick: handleClick }
|
||||
|
||||
return render({
|
||||
props: { ...props, ...propsWeControl },
|
||||
props: { ...props, ...ourProps },
|
||||
slot,
|
||||
attrs,
|
||||
slots,
|
||||
@@ -500,7 +500,7 @@ export let ComboboxButton = defineComponent({
|
||||
open: api.comboboxState.value === ComboboxStates.Open,
|
||||
disabled: api.disabled.value,
|
||||
}
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
ref: api.buttonRef,
|
||||
id,
|
||||
type: type.value,
|
||||
@@ -517,7 +517,7 @@ export let ComboboxButton = defineComponent({
|
||||
}
|
||||
|
||||
return render({
|
||||
props: { ...props, ...propsWeControl },
|
||||
props: { ...props, ...ourProps },
|
||||
slot,
|
||||
attrs,
|
||||
slots,
|
||||
@@ -629,7 +629,7 @@ export let ComboboxInput = defineComponent({
|
||||
|
||||
return () => {
|
||||
let slot = { open: api.comboboxState.value === ComboboxStates.Open }
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
'aria-controls': api.optionsRef.value?.id,
|
||||
'aria-expanded': api.disabled ? undefined : api.comboboxState.value === ComboboxStates.Open,
|
||||
'aria-activedescendant':
|
||||
@@ -647,10 +647,10 @@ export let ComboboxInput = defineComponent({
|
||||
tabIndex: 0,
|
||||
ref: api.inputRef,
|
||||
}
|
||||
let passThroughProps = omit(props, ['displayValue'])
|
||||
let incomingProps = omit(props, ['displayValue'])
|
||||
|
||||
return render({
|
||||
props: { ...passThroughProps, ...propsWeControl },
|
||||
props: { ...incomingProps, ...ourProps },
|
||||
slot,
|
||||
attrs,
|
||||
slots,
|
||||
@@ -709,7 +709,7 @@ export let ComboboxOptions = defineComponent({
|
||||
|
||||
return () => {
|
||||
let slot = { open: api.comboboxState.value === ComboboxStates.Open }
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
'aria-activedescendant':
|
||||
api.activeOptionIndex.value === null
|
||||
? undefined
|
||||
@@ -719,10 +719,10 @@ export let ComboboxOptions = defineComponent({
|
||||
ref: api.optionsRef,
|
||||
role: 'listbox',
|
||||
}
|
||||
let passThroughProps = omit(props, ['hold'])
|
||||
let incomingProps = omit(props, ['hold'])
|
||||
|
||||
return render({
|
||||
props: { ...passThroughProps, ...propsWeControl },
|
||||
props: { ...incomingProps, ...ourProps },
|
||||
slot,
|
||||
attrs,
|
||||
slots,
|
||||
@@ -839,7 +839,7 @@ export let ComboboxOption = defineComponent({
|
||||
return () => {
|
||||
let { disabled } = props
|
||||
let slot = { active: active.value, selected: selected.value, disabled }
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
id,
|
||||
ref: internalOptionRef,
|
||||
role: 'option',
|
||||
@@ -859,7 +859,7 @@ export let ComboboxOption = defineComponent({
|
||||
}
|
||||
|
||||
return render({
|
||||
props: { ...props, ...propsWeControl },
|
||||
props: { ...props, ...ourProps },
|
||||
slot,
|
||||
attrs,
|
||||
slots,
|
||||
|
||||
@@ -78,8 +78,8 @@ export let Description = defineComponent({
|
||||
|
||||
return () => {
|
||||
let { name = 'Description', slot = ref({}), props = {} } = context
|
||||
let passThroughProps = myProps
|
||||
let propsWeControl = {
|
||||
let incomingProps = myProps
|
||||
let ourProps = {
|
||||
...Object.entries(props).reduce(
|
||||
(acc, [key, value]) => Object.assign(acc, { [key]: unref(value) }),
|
||||
{}
|
||||
@@ -88,7 +88,7 @@ export let Description = defineComponent({
|
||||
}
|
||||
|
||||
return render({
|
||||
props: { ...passThroughProps, ...propsWeControl },
|
||||
props: { ...incomingProps, ...ourProps },
|
||||
slot: slot.value,
|
||||
attrs,
|
||||
slots,
|
||||
|
||||
@@ -263,7 +263,7 @@ export let Dialog = defineComponent({
|
||||
}
|
||||
|
||||
return () => {
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
// Manually passthrough the attributes, because Vue can't automatically pass
|
||||
// it to the underlying div because of all the wrapper components below.
|
||||
...attrs,
|
||||
@@ -275,7 +275,7 @@ export let Dialog = defineComponent({
|
||||
'aria-describedby': describedby.value,
|
||||
onClick: handleClick,
|
||||
}
|
||||
let { open: _, initialFocus, ...passThroughProps } = props
|
||||
let { open: _, initialFocus, ...incomingProps } = props
|
||||
|
||||
let slot = { open: dialogState.value === DialogStates.Open }
|
||||
|
||||
@@ -284,7 +284,7 @@ export let Dialog = defineComponent({
|
||||
h(PortalGroup, { target: internalDialogRef.value }, () =>
|
||||
h(ForcePortalRoot, { force: false }, () =>
|
||||
render({
|
||||
props: { ...passThroughProps, ...propsWeControl },
|
||||
props: { ...incomingProps, ...ourProps },
|
||||
slot,
|
||||
attrs,
|
||||
slots,
|
||||
@@ -319,15 +319,15 @@ export let DialogOverlay = defineComponent({
|
||||
}
|
||||
|
||||
return () => {
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
id,
|
||||
'aria-hidden': true,
|
||||
onClick: handleClick,
|
||||
}
|
||||
let passThroughProps = props
|
||||
let incomingProps = props
|
||||
|
||||
return render({
|
||||
props: { ...passThroughProps, ...propsWeControl },
|
||||
props: { ...incomingProps, ...ourProps },
|
||||
slot: { open: api.dialogState.value === DialogStates.Open },
|
||||
attrs,
|
||||
slots,
|
||||
@@ -354,11 +354,11 @@ export let DialogTitle = defineComponent({
|
||||
})
|
||||
|
||||
return () => {
|
||||
let propsWeControl = { id }
|
||||
let passThroughProps = props
|
||||
let ourProps = { id }
|
||||
let incomingProps = props
|
||||
|
||||
return render({
|
||||
props: { ...passThroughProps, ...propsWeControl },
|
||||
props: { ...incomingProps, ...ourProps },
|
||||
slot: { open: api.dialogState.value === DialogStates.Open },
|
||||
attrs,
|
||||
slots,
|
||||
|
||||
@@ -118,9 +118,9 @@ export let Disclosure = defineComponent({
|
||||
)
|
||||
|
||||
return () => {
|
||||
let { defaultOpen: _, ...passThroughProps } = props
|
||||
let { defaultOpen: _, ...incomingProps } = props
|
||||
let slot = { open: disclosureState.value === DisclosureStates.Open, close: api.close }
|
||||
return render({ props: passThroughProps, slot, slots, attrs, name: 'Disclosure' })
|
||||
return render({ props: incomingProps, slot, slots, attrs, name: 'Disclosure' })
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -201,7 +201,7 @@ export let DisclosureButton = defineComponent({
|
||||
|
||||
return () => {
|
||||
let slot = { open: api.disclosureState.value === DisclosureStates.Open }
|
||||
let propsWeControl = isWithinPanel
|
||||
let ourProps = isWithinPanel
|
||||
? {
|
||||
ref: internalButtonRef,
|
||||
type: type.value,
|
||||
@@ -223,7 +223,7 @@ export let DisclosureButton = defineComponent({
|
||||
}
|
||||
|
||||
return render({
|
||||
props: { ...props, ...propsWeControl },
|
||||
props: { ...props, ...ourProps },
|
||||
slot,
|
||||
attrs,
|
||||
slots,
|
||||
@@ -260,10 +260,10 @@ export let DisclosurePanel = defineComponent({
|
||||
|
||||
return () => {
|
||||
let slot = { open: api.disclosureState.value === DisclosureStates.Open, close: api.close }
|
||||
let propsWeControl = { id: api.panelId, ref: api.panel }
|
||||
let ourProps = { id: api.panelId, ref: api.panel }
|
||||
|
||||
return render({
|
||||
props: { ...props, ...propsWeControl },
|
||||
props: { ...props, ...ourProps },
|
||||
slot,
|
||||
attrs,
|
||||
slots,
|
||||
|
||||
@@ -25,11 +25,11 @@ export let FocusTrap = defineComponent({
|
||||
|
||||
return () => {
|
||||
let slot = {}
|
||||
let propsWeControl = { ref: container }
|
||||
let { initialFocus, ...passThroughProps } = props
|
||||
let ourProps = { ref: container }
|
||||
let { initialFocus, ...incomingProps } = props
|
||||
|
||||
return render({
|
||||
props: { ...passThroughProps, ...propsWeControl },
|
||||
props: { ...incomingProps, ...ourProps },
|
||||
slot,
|
||||
attrs,
|
||||
slots,
|
||||
|
||||
@@ -77,15 +77,15 @@ export let Label = defineComponent({
|
||||
|
||||
return () => {
|
||||
let { name = 'Label', slot = {}, props = {} } = context
|
||||
let { passive, ...passThroughProps } = myProps
|
||||
let propsWeControl = {
|
||||
let { passive, ...incomingProps } = myProps
|
||||
let ourProps = {
|
||||
...Object.entries(props).reduce(
|
||||
(acc, [key, value]) => Object.assign(acc, { [key]: unref(value) }),
|
||||
{}
|
||||
),
|
||||
id,
|
||||
}
|
||||
let allProps = { ...passThroughProps, ...propsWeControl }
|
||||
let allProps = { ...incomingProps, ...ourProps }
|
||||
|
||||
// @ts-expect-error props are dynamic via context, some components will
|
||||
// provide an onClick then we can delete it.
|
||||
|
||||
@@ -305,11 +305,11 @@ export let Listbox = defineComponent({
|
||||
)
|
||||
|
||||
return () => {
|
||||
let { name, modelValue, disabled, ...passThroughProps } = props
|
||||
let { name, modelValue, disabled, ...incomingProps } = props
|
||||
|
||||
let slot = { open: listboxState.value === ListboxStates.Open, disabled }
|
||||
let renderConfiguration = {
|
||||
props: omit(passThroughProps, ['onUpdate:modelValue', 'horizontal']),
|
||||
props: omit(incomingProps, ['onUpdate:modelValue', 'horizontal']),
|
||||
slot,
|
||||
slots,
|
||||
attrs,
|
||||
@@ -359,10 +359,10 @@ export let ListboxLabel = defineComponent({
|
||||
open: api.listboxState.value === ListboxStates.Open,
|
||||
disabled: api.disabled.value,
|
||||
}
|
||||
let propsWeControl = { id, ref: api.labelRef, onClick: handleClick }
|
||||
let ourProps = { id, ref: api.labelRef, onClick: handleClick }
|
||||
|
||||
return render({
|
||||
props: { ...props, ...propsWeControl },
|
||||
props: { ...props, ...ourProps },
|
||||
slot,
|
||||
attrs,
|
||||
slots,
|
||||
@@ -444,7 +444,7 @@ export let ListboxButton = defineComponent({
|
||||
open: api.listboxState.value === ListboxStates.Open,
|
||||
disabled: api.disabled.value,
|
||||
}
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
ref: api.buttonRef,
|
||||
id,
|
||||
type: type.value,
|
||||
@@ -461,7 +461,7 @@ export let ListboxButton = defineComponent({
|
||||
}
|
||||
|
||||
return render({
|
||||
props: { ...props, ...propsWeControl },
|
||||
props: { ...props, ...ourProps },
|
||||
slot,
|
||||
attrs,
|
||||
slots,
|
||||
@@ -571,7 +571,7 @@ export let ListboxOptions = defineComponent({
|
||||
|
||||
return () => {
|
||||
let slot = { open: api.listboxState.value === ListboxStates.Open }
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
'aria-activedescendant':
|
||||
api.activeOptionIndex.value === null
|
||||
? undefined
|
||||
@@ -585,10 +585,10 @@ export let ListboxOptions = defineComponent({
|
||||
tabIndex: 0,
|
||||
ref: api.optionsRef,
|
||||
}
|
||||
let passThroughProps = props
|
||||
let incomingProps = props
|
||||
|
||||
return render({
|
||||
props: { ...passThroughProps, ...propsWeControl },
|
||||
props: { ...incomingProps, ...ourProps },
|
||||
slot,
|
||||
attrs,
|
||||
slots,
|
||||
@@ -710,7 +710,7 @@ export let ListboxOption = defineComponent({
|
||||
return () => {
|
||||
let { disabled } = props
|
||||
let slot = { active: active.value, selected: selected.value, disabled }
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
id,
|
||||
ref: internalOptionRef,
|
||||
role: 'option',
|
||||
@@ -730,7 +730,7 @@ export let ListboxOption = defineComponent({
|
||||
}
|
||||
|
||||
return render({
|
||||
props: { ...omit(props, ['value', 'disabled']), ...propsWeControl },
|
||||
props: { ...omit(props, ['value', 'disabled']), ...ourProps },
|
||||
slot,
|
||||
attrs,
|
||||
slots,
|
||||
|
||||
@@ -1358,7 +1358,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
|
||||
|
||||
@@ -302,7 +302,7 @@ export let MenuButton = defineComponent({
|
||||
|
||||
return () => {
|
||||
let slot = { open: api.menuState.value === MenuStates.Open }
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
ref: api.buttonRef,
|
||||
id,
|
||||
type: type.value,
|
||||
@@ -315,7 +315,7 @@ export let MenuButton = defineComponent({
|
||||
}
|
||||
|
||||
return render({
|
||||
props: { ...props, ...propsWeControl },
|
||||
props: { ...props, ...ourProps },
|
||||
slot,
|
||||
attrs,
|
||||
slots,
|
||||
@@ -443,7 +443,7 @@ export let MenuItems = defineComponent({
|
||||
|
||||
return () => {
|
||||
let slot = { open: api.menuState.value === MenuStates.Open }
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
'aria-activedescendant':
|
||||
api.activeItemIndex.value === null
|
||||
? undefined
|
||||
@@ -457,10 +457,10 @@ export let MenuItems = defineComponent({
|
||||
ref: api.itemsRef,
|
||||
}
|
||||
|
||||
let passThroughProps = props
|
||||
let incomingProps = props
|
||||
|
||||
return render({
|
||||
props: { ...passThroughProps, ...propsWeControl },
|
||||
props: { ...incomingProps, ...ourProps },
|
||||
slot,
|
||||
attrs,
|
||||
slots,
|
||||
@@ -537,7 +537,7 @@ export let MenuItem = defineComponent({
|
||||
return () => {
|
||||
let { disabled } = props
|
||||
let slot = { active: active.value, disabled }
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
id,
|
||||
ref: internalItemRef,
|
||||
role: 'menuitem',
|
||||
@@ -552,7 +552,7 @@ export let MenuItem = defineComponent({
|
||||
}
|
||||
|
||||
return render({
|
||||
props: { ...props, ...propsWeControl },
|
||||
props: { ...props, ...ourProps },
|
||||
slot,
|
||||
attrs,
|
||||
slots,
|
||||
|
||||
@@ -379,7 +379,7 @@ export let PopoverButton = defineComponent({
|
||||
|
||||
return () => {
|
||||
let slot = { open: api.popoverState.value === PopoverStates.Open }
|
||||
let propsWeControl = isWithinPanel
|
||||
let ourProps = isWithinPanel
|
||||
? {
|
||||
ref: elementRef,
|
||||
type: type.value,
|
||||
@@ -401,7 +401,7 @@ export let PopoverButton = defineComponent({
|
||||
}
|
||||
|
||||
return render({
|
||||
props: { ...props, ...propsWeControl },
|
||||
props: { ...props, ...ourProps },
|
||||
slot,
|
||||
attrs: attrs,
|
||||
slots: slots,
|
||||
@@ -439,14 +439,14 @@ export let PopoverOverlay = defineComponent({
|
||||
|
||||
return () => {
|
||||
let slot = { open: api.popoverState.value === PopoverStates.Open }
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
id,
|
||||
'aria-hidden': true,
|
||||
onClick: handleClick,
|
||||
}
|
||||
|
||||
return render({
|
||||
props: { ...props, ...propsWeControl },
|
||||
props: { ...props, ...ourProps },
|
||||
slot,
|
||||
attrs,
|
||||
slots,
|
||||
@@ -581,14 +581,14 @@ export let PopoverPanel = defineComponent({
|
||||
close: api.close,
|
||||
}
|
||||
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
ref: api.panel,
|
||||
id: api.panelId,
|
||||
onKeydown: handleKeyDown,
|
||||
}
|
||||
|
||||
return render({
|
||||
props: { ...props, ...propsWeControl },
|
||||
props: { ...props, ...ourProps },
|
||||
slot,
|
||||
attrs,
|
||||
slots,
|
||||
@@ -656,10 +656,10 @@ export let PopoverGroup = defineComponent({
|
||||
})
|
||||
|
||||
return () => {
|
||||
let propsWeControl = { ref: groupRef }
|
||||
let ourProps = { ref: groupRef }
|
||||
|
||||
return render({
|
||||
props: { ...props, ...propsWeControl },
|
||||
props: { ...props, ...ourProps },
|
||||
slot: {},
|
||||
attrs,
|
||||
slots,
|
||||
|
||||
@@ -73,7 +73,7 @@ export let Portal = defineComponent({
|
||||
return () => {
|
||||
if (myTarget.value === null) return null
|
||||
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
ref: element,
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ export let Portal = defineComponent({
|
||||
Teleport,
|
||||
{ to: myTarget.value },
|
||||
render({
|
||||
props: { ...props, ...propsWeControl },
|
||||
props: { ...props, ...ourProps },
|
||||
slot: {},
|
||||
attrs,
|
||||
slots,
|
||||
@@ -116,9 +116,9 @@ export let PortalGroup = defineComponent({
|
||||
provide(PortalGroupContext, api)
|
||||
|
||||
return () => {
|
||||
let { target: _, ...passThroughProps } = props
|
||||
let { target: _, ...incomingProps } = props
|
||||
|
||||
return render({ props: passThroughProps, slot: {}, attrs, slots, name: 'PortalGroup' })
|
||||
return render({ props: incomingProps, slot: {}, attrs, slots, name: 'PortalGroup' })
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@@ -191,9 +191,9 @@ export let RadioGroup = defineComponent({
|
||||
let id = `headlessui-radiogroup-${useId()}`
|
||||
|
||||
return () => {
|
||||
let { modelValue, disabled, name, ...passThroughProps } = props
|
||||
let { modelValue, disabled, name, ...incomingProps } = props
|
||||
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
ref: radioGroupRef,
|
||||
id,
|
||||
role: 'radiogroup',
|
||||
@@ -203,7 +203,7 @@ export let RadioGroup = defineComponent({
|
||||
}
|
||||
|
||||
let renderConfiguration = {
|
||||
props: { ...passThroughProps, ...propsWeControl },
|
||||
props: { ...incomingProps, ...ourProps },
|
||||
slot: {},
|
||||
attrs,
|
||||
slots,
|
||||
@@ -290,7 +290,7 @@ export let RadioGroupOption = defineComponent({
|
||||
}
|
||||
|
||||
return () => {
|
||||
let passThroughProps = omit(props, ['value', 'disabled'])
|
||||
let incomingProps = omit(props, ['value', 'disabled'])
|
||||
|
||||
let slot = {
|
||||
checked: checked.value,
|
||||
@@ -298,7 +298,7 @@ export let RadioGroupOption = defineComponent({
|
||||
active: Boolean(state.value & OptionState.Active),
|
||||
}
|
||||
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
id,
|
||||
ref: optionRef,
|
||||
role: 'radio',
|
||||
@@ -313,7 +313,7 @@ export let RadioGroupOption = defineComponent({
|
||||
}
|
||||
|
||||
return render({
|
||||
props: { ...passThroughProps, ...propsWeControl },
|
||||
props: { ...incomingProps, ...ourProps },
|
||||
slot,
|
||||
attrs,
|
||||
slots,
|
||||
|
||||
@@ -103,9 +103,9 @@ export let Switch = defineComponent({
|
||||
}
|
||||
|
||||
return () => {
|
||||
let { name, value, modelValue, ...passThroughProps } = props
|
||||
let { name, value, modelValue, ...incomingProps } = props
|
||||
let slot = { checked: modelValue }
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
id,
|
||||
ref: switchRef,
|
||||
role: 'switch',
|
||||
@@ -120,7 +120,7 @@ export let Switch = defineComponent({
|
||||
}
|
||||
|
||||
let renderConfiguration = {
|
||||
props: { ...passThroughProps, ...propsWeControl },
|
||||
props: { ...incomingProps, ...ourProps },
|
||||
slot,
|
||||
attrs,
|
||||
slots,
|
||||
|
||||
@@ -176,14 +176,14 @@ export let TabList = defineComponent({
|
||||
return () => {
|
||||
let slot = { selectedIndex: api.selectedIndex.value }
|
||||
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
role: 'tablist',
|
||||
'aria-orientation': api.orientation.value,
|
||||
}
|
||||
let passThroughProps = props
|
||||
let incomingProps = props
|
||||
|
||||
return render({
|
||||
props: { ...passThroughProps, ...propsWeControl },
|
||||
props: { ...incomingProps, ...ourProps },
|
||||
slot,
|
||||
attrs,
|
||||
slots,
|
||||
@@ -281,7 +281,7 @@ export let Tab = defineComponent({
|
||||
|
||||
return () => {
|
||||
let slot = { selected: selected.value }
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
ref: internalTabRef,
|
||||
onKeydown: handleKeyDown,
|
||||
onFocus: api.activation.value === 'manual' ? handleFocus : handleSelection,
|
||||
@@ -297,7 +297,7 @@ export let Tab = defineComponent({
|
||||
}
|
||||
|
||||
return render({
|
||||
props: { ...props, ...propsWeControl },
|
||||
props: { ...props, ...ourProps },
|
||||
slot,
|
||||
attrs,
|
||||
slots,
|
||||
@@ -354,7 +354,7 @@ export let TabPanel = defineComponent({
|
||||
|
||||
return () => {
|
||||
let slot = { selected: selected.value }
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
ref: internalPanelRef,
|
||||
id,
|
||||
role: 'tabpanel',
|
||||
@@ -363,7 +363,7 @@ export let TabPanel = defineComponent({
|
||||
}
|
||||
|
||||
return render({
|
||||
props: { ...props, ...propsWeControl },
|
||||
props: { ...props, ...ourProps },
|
||||
slot,
|
||||
attrs,
|
||||
slots,
|
||||
|
||||
@@ -326,11 +326,11 @@ export let TransitionChild = defineComponent({
|
||||
...rest
|
||||
} = props
|
||||
|
||||
let propsWeControl = { ref: container }
|
||||
let passthroughProps = rest
|
||||
let ourProps = { ref: container }
|
||||
let incomingProps = rest
|
||||
|
||||
return render({
|
||||
props: { ...passthroughProps, ...propsWeControl },
|
||||
props: { ...incomingProps, ...ourProps },
|
||||
slot: {},
|
||||
slots,
|
||||
attrs,
|
||||
@@ -416,7 +416,7 @@ export let TransitionRoot = defineComponent({
|
||||
provide(TransitionContext, transitionBag)
|
||||
|
||||
return () => {
|
||||
let passThroughProps = omit(props, ['show', 'appear', 'unmount'])
|
||||
let incomingProps = omit(props, ['show', 'appear', 'unmount'])
|
||||
let sharedProps = { unmount: props.unmount }
|
||||
|
||||
return render({
|
||||
@@ -437,7 +437,7 @@ export let TransitionRoot = defineComponent({
|
||||
onAfterLeave: () => emit('afterLeave'),
|
||||
...attrs,
|
||||
...sharedProps,
|
||||
...passThroughProps,
|
||||
...incomingProps,
|
||||
},
|
||||
slots.default
|
||||
),
|
||||
|
||||
@@ -24,8 +24,8 @@ export let ForcePortalRoot = defineComponent({
|
||||
provide(ForcePortalRootContext, props.force)
|
||||
|
||||
return () => {
|
||||
let { force, ...passThroughProps } = props
|
||||
return render({ props: passThroughProps, slot: {}, slots, attrs, name: 'ForcePortalRoot' })
|
||||
let { force, ...incomingProps } = props
|
||||
return render({ props: incomingProps, slot: {}, slots, attrs, name: 'ForcePortalRoot' })
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@@ -8,7 +8,7 @@ export let VisuallyHidden = defineComponent({
|
||||
},
|
||||
setup(props, { slots, attrs }) {
|
||||
return () => {
|
||||
let propsWeControl = {
|
||||
let ourProps = {
|
||||
style: {
|
||||
position: 'absolute',
|
||||
width: 1,
|
||||
@@ -23,7 +23,7 @@ export let VisuallyHidden = defineComponent({
|
||||
}
|
||||
|
||||
return render({
|
||||
props: { ...props, ...propsWeControl },
|
||||
props: { ...props, ...ourProps },
|
||||
slot: {},
|
||||
attrs,
|
||||
slots,
|
||||
|
||||
@@ -81,12 +81,12 @@ function _render({
|
||||
slots: Slots
|
||||
name: string
|
||||
}) {
|
||||
let { as, ...passThroughProps } = omit(props, ['unmount', 'static'])
|
||||
let { as, ...incomingProps } = omit(props, ['unmount', 'static'])
|
||||
|
||||
let children = slots.default?.(slot)
|
||||
|
||||
if (as === 'template') {
|
||||
if (Object.keys(passThroughProps).length > 0 || Object.keys(attrs).length > 0) {
|
||||
if (Object.keys(incomingProps).length > 0 || Object.keys(attrs).length > 0) {
|
||||
let [firstChild, ...other] = children ?? []
|
||||
|
||||
if (!isValidElement(firstChild) || other.length > 0) {
|
||||
@@ -96,7 +96,7 @@ function _render({
|
||||
'',
|
||||
`The current component <${name} /> is rendering a "template".`,
|
||||
`However we need to passthrough the following props:`,
|
||||
Object.keys(passThroughProps)
|
||||
Object.keys(incomingProps)
|
||||
.concat(Object.keys(attrs))
|
||||
.map((line) => ` - ${line}`)
|
||||
.join('\n'),
|
||||
@@ -112,7 +112,7 @@ function _render({
|
||||
)
|
||||
}
|
||||
|
||||
return cloneVNode(firstChild, passThroughProps as Record<string, any>)
|
||||
return cloneVNode(firstChild, incomingProps as Record<string, any>)
|
||||
}
|
||||
|
||||
if (Array.isArray(children) && children.length === 1) {
|
||||
@@ -122,7 +122,7 @@ function _render({
|
||||
return children
|
||||
}
|
||||
|
||||
return h(as, passThroughProps, children)
|
||||
return h(as, incomingProps, children)
|
||||
}
|
||||
|
||||
export function compact<T extends Record<any, any>>(object: T) {
|
||||
|
||||
Reference in New Issue
Block a user