diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cb602f..674dff4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Add `Combobox` component ([#1047](https://github.com/tailwindlabs/headlessui/pull/1047), [#1099](https://github.com/tailwindlabs/headlessui/pull/1099), [#1101](https://github.com/tailwindlabs/headlessui/pull/1101), [#1104](https://github.com/tailwindlabs/headlessui/pull/1104)) +- Add `Combobox` component ([#1047](https://github.com/tailwindlabs/headlessui/pull/1047), [#1099](https://github.com/tailwindlabs/headlessui/pull/1099), [#1101](https://github.com/tailwindlabs/headlessui/pull/1101), [#1104](https://github.com/tailwindlabs/headlessui/pull/1104), [#1109](https://github.com/tailwindlabs/headlessui/pull/1109)) ## [Unreleased - @headlessui/vue] @@ -32,7 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Add `Combobox` component ([#1047](https://github.com/tailwindlabs/headlessui/pull/1047), [#1099](https://github.com/tailwindlabs/headlessui/pull/1099), [#1101](https://github.com/tailwindlabs/headlessui/pull/1101), [#1104](https://github.com/tailwindlabs/headlessui/pull/1104), [#1106](https://github.com/tailwindlabs/headlessui/pull/1106)) +- Add `Combobox` component ([#1047](https://github.com/tailwindlabs/headlessui/pull/1047), [#1099](https://github.com/tailwindlabs/headlessui/pull/1099), [#1101](https://github.com/tailwindlabs/headlessui/pull/1101), [#1104](https://github.com/tailwindlabs/headlessui/pull/1104), [#1106](https://github.com/tailwindlabs/headlessui/pull/1106), [#1109](https://github.com/tailwindlabs/headlessui/pull/1109)) ## [@headlessui/react@v1.4.3] - 2022-01-14 diff --git a/packages/@headlessui-react/src/components/combobox/combobox.test.tsx b/packages/@headlessui-react/src/components/combobox/combobox.test.tsx index f68da59..b643d1d 100644 --- a/packages/@headlessui-react/src/components/combobox/combobox.test.tsx +++ b/packages/@headlessui-react/src/components/combobox/combobox.test.tsx @@ -4269,10 +4269,10 @@ describe('Mouse interactions', () => { 'should be possible to hold the last active option', suppressConsoleLogs(async () => { render( - + Trigger - + Option A Option B Option C diff --git a/packages/@headlessui-react/src/components/combobox/combobox.tsx b/packages/@headlessui-react/src/components/combobox/combobox.tsx index 3445153..a47349c 100644 --- a/packages/@headlessui-react/src/components/combobox/combobox.tsx +++ b/packages/@headlessui-react/src/components/combobox/combobox.tsx @@ -53,13 +53,13 @@ interface StateDefinition { comboboxPropsRef: MutableRefObject<{ value: unknown onChange(value: unknown): void - hold: boolean }> inputPropsRef: MutableRefObject<{ displayValue?(item: unknown): string }> optionsPropsRef: MutableRefObject<{ static: boolean + hold: boolean }> labelRef: MutableRefObject inputRef: MutableRefObject @@ -230,22 +230,23 @@ let ComboboxRoot = forwardRefWithAs(function Combobox< TTag extends ElementType = typeof DEFAULT_COMBOBOX_TAG, TType = string >( - props: Props, 'value' | 'onChange' | 'disabled' | 'hold'> & { + props: Props, 'value' | 'onChange' | 'disabled'> & { value: TType onChange(value: TType): void disabled?: boolean - hold?: boolean }, ref: Ref ) { - let { value, onChange, disabled = false, hold = false, ...passThroughProps } = props + let { value, onChange, disabled = false, ...passThroughProps } = props let comboboxPropsRef = useRef({ value, onChange, - hold, }) - let optionsPropsRef = useRef({ static: false }) + let optionsPropsRef = useRef({ + static: false, + hold: false, + }) let inputPropsRef = useRef({ displayValue: undefined, }) @@ -272,9 +273,6 @@ let ComboboxRoot = forwardRefWithAs(function Combobox< useIsoMorphicEffect(() => { comboboxPropsRef.current.onChange = onChange }, [onChange, comboboxPropsRef]) - useIsoMorphicEffect(() => { - comboboxPropsRef.current.hold = hold - }, [hold, comboboxPropsRef]) useIsoMorphicEffect(() => dispatch({ type: ActionTypes.SetDisabled, disabled }), [disabled]) @@ -710,6 +708,7 @@ interface OptionsRenderPropArg { type OptionsPropsWeControl = | 'aria-activedescendant' | 'aria-labelledby' + | 'hold' | 'id' | 'onKeyDown' | 'role' @@ -721,9 +720,12 @@ let Options = forwardRefWithAs(function Options< TTag extends ElementType = typeof DEFAULT_OPTIONS_TAG >( props: Props & - PropsForFeatures, + PropsForFeatures & { + hold?: boolean + }, ref: Ref ) { + let { hold = false, ...passthroughProps } = props let [state] = useComboboxContext('Combobox.Options') let { optionsPropsRef } = state @@ -743,6 +745,9 @@ let Options = forwardRefWithAs(function Options< useIsoMorphicEffect(() => { optionsPropsRef.current.static = props.static ?? false }, [optionsPropsRef, props.static]) + useIsoMorphicEffect(() => { + optionsPropsRef.current.hold = hold + }, [hold, optionsPropsRef]) useTreeWalker({ container: state.optionsRef.current, @@ -774,7 +779,6 @@ let Options = forwardRefWithAs(function Options< id, ref: optionsRef, } - let passthroughProps = props return render({ props: { ...passthroughProps, ...propsWeControl }, @@ -880,7 +884,7 @@ function Option< let handleLeave = useCallback(() => { if (disabled) return if (!active) return - if (state.comboboxPropsRef.current.hold) return + if (state.optionsPropsRef.current.hold) return dispatch({ type: ActionTypes.GoToOption, focus: Focus.Nothing }) }, [disabled, active, dispatch, state.comboboxState, state.comboboxPropsRef]) diff --git a/packages/@headlessui-vue/src/components/combobox/combobox.test.ts b/packages/@headlessui-vue/src/components/combobox/combobox.test.ts index cdc72b8..6c2f028 100644 --- a/packages/@headlessui-vue/src/components/combobox/combobox.test.ts +++ b/packages/@headlessui-vue/src/components/combobox/combobox.test.ts @@ -4491,10 +4491,10 @@ describe('Mouse interactions', () => { suppressConsoleLogs(async () => { renderTemplate({ template: html` - + Trigger - + Option A Option B Option C diff --git a/packages/@headlessui-vue/src/components/combobox/combobox.ts b/packages/@headlessui-vue/src/components/combobox/combobox.ts index b71b8f3..5f0d934 100644 --- a/packages/@headlessui-vue/src/components/combobox/combobox.ts +++ b/packages/@headlessui-vue/src/components/combobox/combobox.ts @@ -37,10 +37,9 @@ type StateDefinition = { // State comboboxState: Ref value: ComputedRef - hold: ComputedRef inputPropsRef: Ref<{ displayValue?: (item: unknown) => string }> - optionsPropsRef: Ref<{ static: boolean }> + optionsPropsRef: Ref<{ static: boolean; hold: boolean }> labelRef: Ref inputRef: Ref @@ -85,7 +84,6 @@ export let Combobox = defineComponent({ as: { type: [Object, String], default: 'template' }, disabled: { type: [Boolean], default: false }, modelValue: { type: [Object, String, Number, Boolean] }, - hold: { type: [Boolean], default: false }, }, setup(props, { slots, attrs, emit }) { let comboboxState = ref(ComboboxStates.Closed) @@ -97,17 +95,16 @@ export let Combobox = defineComponent({ ) as StateDefinition['optionsRef'] let optionsPropsRef = ref({ static: false, + hold: false, }) as StateDefinition['optionsPropsRef'] let options = ref([]) let activeOptionIndex = ref(null) let value = computed(() => props.modelValue) - let hold = computed(() => props.hold) let api = { comboboxState, value, - hold, inputRef, labelRef, buttonRef, @@ -260,7 +257,7 @@ export let Combobox = defineComponent({ } return render({ - props: omit(props, ['modelValue', 'onUpdate:modelValue', 'disabled', 'hold']), + props: omit(props, ['modelValue', 'onUpdate:modelValue', 'disabled']), slot, slots, attrs, @@ -545,12 +542,16 @@ export let ComboboxOptions = defineComponent({ as: { type: [Object, String], default: 'ul' }, static: { type: Boolean, default: false }, unmount: { type: Boolean, default: true }, + hold: { type: [Boolean], default: false }, }, setup(props, { attrs, slots }) { let api = useComboboxContext('ComboboxOptions') let id = `headlessui-combobox-options-${useId()}` watchEffect(() => { - api.optionsPropsRef.value.static = props.static ?? false + api.optionsPropsRef.value.static = props.static + }) + watchEffect(() => { + api.optionsPropsRef.value.hold = props.hold }) let usesOpenClosedState = useOpenClosed() let visible = computed(() => { @@ -586,7 +587,7 @@ export let ComboboxOptions = defineComponent({ ref: api.optionsRef, role: 'listbox', } - let passThroughProps = props + let passThroughProps = omit(props, ['hold']) return render({ props: { ...passThroughProps, ...propsWeControl }, @@ -667,7 +668,7 @@ export let ComboboxOption = defineComponent({ function handleLeave() { if (props.disabled) return if (!active.value) return - if (api.hold.value) return + if (api.optionsPropsRef.value.hold) return api.goToOption(Focus.Nothing) }