diff --git a/CHANGELOG.md b/CHANGELOG.md index 3cc279e8ea..9c83e566e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Only activate the `Tab` on mouseup ([#1192](https://github.com/tailwindlabs/headlessui/pull/1192)) - Ignore "outside click" on removed elements ([#1193](https://github.com/tailwindlabs/headlessui/pull/1193)) - Remove `focus()` from Listbox Option ([#1218](https://github.com/tailwindlabs/headlessui/pull/1218)) +- Improve some internal code ([#1221](https://github.com/tailwindlabs/headlessui/pull/1221)) ### Added @@ -46,6 +47,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Only activate the `Tab` on mouseup ([#1192](https://github.com/tailwindlabs/headlessui/pull/1192)) - Ignore "outside click" on removed elements ([#1193](https://github.com/tailwindlabs/headlessui/pull/1193)) - Remove `focus()` from Listbox Option ([#1218](https://github.com/tailwindlabs/headlessui/pull/1218)) +- Improve some internal code ([#1221](https://github.com/tailwindlabs/headlessui/pull/1221)) ### Added diff --git a/packages/@headlessui-react/src/components/combobox/combobox.tsx b/packages/@headlessui-react/src/components/combobox/combobox.tsx index e23a069f9f..a61689e2df 100644 --- a/packages/@headlessui-react/src/components/combobox/combobox.tsx +++ b/packages/@headlessui-react/src/components/combobox/combobox.tsx @@ -895,8 +895,8 @@ let Option = forwardRefWithAs(function Option< bag.current.value = value }, [bag, value]) useIsoMorphicEffect(() => { - bag.current.textValue = document.getElementById(id)?.textContent?.toLowerCase() - }, [bag, id]) + bag.current.textValue = internalOptionRef.current?.textContent?.toLowerCase() + }, [bag, internalOptionRef]) let select = useCallback(() => actions.selectOption(id), [actions, id]) @@ -928,10 +928,10 @@ let Option = forwardRefWithAs(function Option< if (state.activationTrigger === ActivationTrigger.Pointer) return let d = disposables() d.requestAnimationFrame(() => { - document.getElementById(id)?.scrollIntoView?.({ block: 'nearest' }) + internalOptionRef.current?.scrollIntoView?.({ block: 'nearest' }) }) return d.dispose - }, [id, active, state.comboboxState, state.activationTrigger, /* We also want to trigger this when the position of the active item changes so that we can re-trigger the scrollIntoView */ state.activeOptionIndex]) + }, [internalOptionRef, active, state.comboboxState, state.activationTrigger, /* We also want to trigger this when the position of the active item changes so that we can re-trigger the scrollIntoView */ state.activeOptionIndex]) let handleClick = useCallback( (event: { preventDefault: Function }) => { diff --git a/packages/@headlessui-react/src/components/disclosure/disclosure.tsx b/packages/@headlessui-react/src/components/disclosure/disclosure.tsx index 5ae2d571a6..874d50d545 100644 --- a/packages/@headlessui-react/src/components/disclosure/disclosure.tsx +++ b/packages/@headlessui-react/src/components/disclosure/disclosure.tsx @@ -39,6 +39,9 @@ interface StateDefinition { linkedPanel: boolean + buttonRef: MutableRefObject + panelRef: MutableRefObject + buttonId: string panelId: string } @@ -157,9 +160,14 @@ let DisclosureRoot = forwardRefWithAs(function Disclosure< let panelId = `headlessui-disclosure-panel-${useId()}` let disclosureRef = useSyncRefs(ref) + let panelRef = useRef(null) + let buttonRef = useRef(null) + let reducerBag = useReducer(stateReducer, { disclosureState: defaultOpen ? DisclosureStates.Open : DisclosureStates.Closed, linkedPanel: false, + buttonRef, + panelRef, buttonId, panelId, } as StateDefinition) @@ -232,12 +240,12 @@ let Button = forwardRefWithAs(function Button ) { let [state, dispatch] = useDisclosureContext('Disclosure.Button') - let internalButtonRef = useRef(null) - let buttonRef = useSyncRefs(internalButtonRef, ref) - let panelContext = useDisclosurePanelContext() let isWithinPanel = panelContext === null ? false : panelContext === state.panelId + let internalButtonRef = useRef(null) + let buttonRef = useSyncRefs(internalButtonRef, ref, !isWithinPanel ? state.buttonRef : null) + let handleKeyDown = useCallback( (event: ReactKeyboardEvent) => { if (isWithinPanel) { @@ -249,7 +257,7 @@ let Button = forwardRefWithAs(function Button) => { @@ -284,12 +292,12 @@ let Button = forwardRefWithAs(function Button( @@ -341,7 +349,7 @@ let Panel = forwardRefWithAs(function Panel { + let panelRef = useSyncRefs(ref, state.panelRef, () => { if (state.linkedPanel) return dispatch({ type: ActionTypes.LinkPanel }) }) diff --git a/packages/@headlessui-react/src/components/menu/menu.tsx b/packages/@headlessui-react/src/components/menu/menu.tsx index 253bd9646e..dcaf4265c0 100644 --- a/packages/@headlessui-react/src/components/menu/menu.tsx +++ b/packages/@headlessui-react/src/components/menu/menu.tsx @@ -453,8 +453,8 @@ let Items = forwardRefWithAs(function Items state.buttonRef.current?.focus({ preventScroll: true })) break @@ -580,20 +580,19 @@ let Item = forwardRefWithAs(function Item { - document.getElementById(id)?.scrollIntoView?.({ block: 'nearest' }) + internalItemRef.current?.scrollIntoView?.({ block: 'nearest' }) }) return d.dispose - }, [id, active, state.menuState, state.activationTrigger, /* We also want to trigger this when the position of the active item changes so that we can re-trigger the scrollIntoView */ state.activeItemIndex]) + }, [internalItemRef, active, state.menuState, state.activationTrigger, /* We also want to trigger this when the position of the active item changes so that we can re-trigger the scrollIntoView */ state.activeItemIndex]) let bag = useRef({ disabled, domRef: internalItemRef }) useIsoMorphicEffect(() => { bag.current.disabled = disabled }, [bag, disabled]) - useIsoMorphicEffect(() => { - bag.current.textValue = document.getElementById(id)?.textContent?.toLowerCase() - }, [bag, id]) + bag.current.textValue = internalItemRef.current?.textContent?.toLowerCase() + }, [bag, internalItemRef]) useIsoMorphicEffect(() => { dispatch({ type: ActionTypes.RegisterItem, id, dataRef: bag }) diff --git a/packages/@headlessui-vue/src/components/combobox/combobox.ts b/packages/@headlessui-vue/src/components/combobox/combobox.ts index e1576369cf..2e9ca74749 100644 --- a/packages/@headlessui-vue/src/components/combobox/combobox.ts +++ b/packages/@headlessui-vue/src/components/combobox/combobox.ts @@ -708,7 +708,7 @@ export let ComboboxOption = defineComponent({ if (api.comboboxState.value !== ComboboxStates.Open) return if (!active.value) return if (api.activationTrigger.value === ActivationTrigger.Pointer) return - nextTick(() => document.getElementById(id)?.scrollIntoView?.({ block: 'nearest' })) + nextTick(() => dom(internalOptionRef)?.scrollIntoView?.({ block: 'nearest' })) }) function handleClick(event: MouseEvent) { diff --git a/packages/@headlessui-vue/src/components/disclosure/disclosure.ts b/packages/@headlessui-vue/src/components/disclosure/disclosure.ts index 2f6c5fec18..73c96fd1cc 100644 --- a/packages/@headlessui-vue/src/components/disclosure/disclosure.ts +++ b/packages/@headlessui-vue/src/components/disclosure/disclosure.ts @@ -139,17 +139,17 @@ export let DisclosureButton = defineComponent({ let panelContext = useDisclosurePanelContext() let isWithinPanel = panelContext === null ? false : panelContext === api.panelId - let elementRef = ref(null) + let internalButtonRef = ref(null) if (!isWithinPanel) { watchEffect(() => { - api.button.value = elementRef.value + api.button.value = internalButtonRef.value }) } let type = useResolveButtonType( computed(() => ({ as: props.as, type: attrs.type })), - elementRef + internalButtonRef ) function handleClick() { @@ -201,14 +201,14 @@ export let DisclosureButton = defineComponent({ let slot = { open: api.disclosureState.value === DisclosureStates.Open } let propsWeControl = isWithinPanel ? { - ref: elementRef, + ref: internalButtonRef, type: type.value, onClick: handleClick, onKeydown: handleKeyDown, } : { id: api.buttonId, - ref: elementRef, + ref: internalButtonRef, type: type.value, 'aria-expanded': props.disabled ? undefined diff --git a/packages/@headlessui-vue/src/components/menu/menu.ts b/packages/@headlessui-vue/src/components/menu/menu.ts index 631b1a9c7e..24051b5d5c 100644 --- a/packages/@headlessui-vue/src/components/menu/menu.ts +++ b/packages/@headlessui-vue/src/components/menu/menu.ts @@ -366,8 +366,9 @@ export let MenuItems = defineComponent({ event.preventDefault() event.stopPropagation() if (api.activeItemIndex.value !== null) { - let { id } = api.items.value[api.activeItemIndex.value] - document.getElementById(id)?.click() + let activeItem = api.items.value[api.activeItemIndex.value] + let _activeItem = activeItem as unknown as UnwrapNestedRefs + dom(_activeItem.dataRef.domRef)?.click() } api.closeMenu() nextTick(() => dom(api.buttonRef)?.focus({ preventScroll: true })) @@ -490,7 +491,7 @@ export let MenuItem = defineComponent({ domRef: internalItemRef, })) onMounted(() => { - let textValue = document.getElementById(id)?.textContent?.toLowerCase().trim() + let textValue = dom(internalItemRef)?.textContent?.toLowerCase().trim() if (textValue !== undefined) dataRef.value.textValue = textValue }) @@ -501,7 +502,7 @@ export let MenuItem = defineComponent({ if (api.menuState.value !== MenuStates.Open) return if (!active.value) return if (api.activationTrigger.value === ActivationTrigger.Pointer) return - nextTick(() => document.getElementById(id)?.scrollIntoView?.({ block: 'nearest' })) + nextTick(() => dom(internalItemRef)?.scrollIntoView?.({ block: 'nearest' })) }) function handleClick(event: MouseEvent) {