diff --git a/packages/@headlessui-react/CHANGELOG.md b/packages/@headlessui-react/CHANGELOG.md index 70eff7a325..de7c914f74 100644 --- a/packages/@headlessui-react/CHANGELOG.md +++ b/packages/@headlessui-react/CHANGELOG.md @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Resync input when display value changes ([#1679](https://github.com/tailwindlabs/headlessui/pull/1679)) - Ensure controlled `Tabs` don't change automagically ([#1680](https://github.com/tailwindlabs/headlessui/pull/1680)) - Don't scroll lock when a Transition + Dialog is mounted but hidden ([#1681](https://github.com/tailwindlabs/headlessui/pull/1681)) +- Improve outside click on Safari iOS ([#1712](https://github.com/tailwindlabs/headlessui/pull/1712)) ## [1.6.6] - 2022-07-07 diff --git a/packages/@headlessui-react/src/hooks/use-document-event.ts b/packages/@headlessui-react/src/hooks/use-document-event.ts new file mode 100644 index 0000000000..05409940db --- /dev/null +++ b/packages/@headlessui-react/src/hooks/use-document-event.ts @@ -0,0 +1,20 @@ +import { useEffect } from 'react' + +import { useLatestValue } from './use-latest-value' + +export function useDocumentEvent( + type: TType, + listener: (ev: DocumentEventMap[TType]) => any, + options?: boolean | AddEventListenerOptions +) { + let listenerRef = useLatestValue(listener) + + useEffect(() => { + function handler(event: DocumentEventMap[TType]) { + listenerRef.current(event) + } + + document.addEventListener(type, handler, options) + return () => document.removeEventListener(type, handler, options) + }, [type, options]) +} diff --git a/packages/@headlessui-react/src/hooks/use-outside-click.ts b/packages/@headlessui-react/src/hooks/use-outside-click.ts index 30c5e8d9f1..d877d963c1 100644 --- a/packages/@headlessui-react/src/hooks/use-outside-click.ts +++ b/packages/@headlessui-react/src/hooks/use-outside-click.ts @@ -1,6 +1,6 @@ import { MutableRefObject, useEffect, useRef } from 'react' import { FocusableMode, isFocusableElement } from '../utils/focus-management' -import { useWindowEvent } from './use-window-event' +import { useDocumentEvent } from './use-document-event' type Container = MutableRefObject | HTMLElement | null type ContainerCollection = Container[] | Set @@ -92,7 +92,7 @@ export function useOutsideClick( let initialClickTarget = useRef(null) - useWindowEvent( + useDocumentEvent( 'mousedown', (event) => { if (enabledRef.current) { @@ -102,7 +102,7 @@ export function useOutsideClick( true ) - useWindowEvent( + useDocumentEvent( 'click', (event) => { if (!initialClickTarget.current) { @@ -130,7 +130,7 @@ export function useOutsideClick( // In this case we care only about the first case so we check to see if the active element is the iframe // If so this was because of a click, focus, or other interaction with the child iframe // and we can consider it an "outside click" - useWindowEvent( + useDocumentEvent( 'blur', (event) => handleOutsideClick(event, () => diff --git a/packages/@headlessui-vue/CHANGELOG.md b/packages/@headlessui-vue/CHANGELOG.md index a1ad166266..37eb691226 100644 --- a/packages/@headlessui-vue/CHANGELOG.md +++ b/packages/@headlessui-vue/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Close `Menu` component when using `tab` key ([#1673](https://github.com/tailwindlabs/headlessui/pull/1673)) - Resync input when display value changes ([#1679](https://github.com/tailwindlabs/headlessui/pull/1679)) - Ensure controlled `Tabs` don't change automagically ([#1680](https://github.com/tailwindlabs/headlessui/pull/1680)) +- Improve outside click on Safari iOS ([#1712](https://github.com/tailwindlabs/headlessui/pull/1712)) ## [1.6.7] - 2022-07-12 diff --git a/packages/@headlessui-vue/src/hooks/use-document-event.ts b/packages/@headlessui-vue/src/hooks/use-document-event.ts new file mode 100644 index 0000000000..b0aa33dd72 --- /dev/null +++ b/packages/@headlessui-vue/src/hooks/use-document-event.ts @@ -0,0 +1,15 @@ +import { watchEffect } from 'vue' +import { isServer } from '../utils/ssr' + +export function useDocumentEvent( + type: TType, + listener: (this: Document, ev: DocumentEventMap[TType]) => any, + options?: boolean | AddEventListenerOptions +) { + if (isServer) return + + watchEffect((onInvalidate) => { + document.addEventListener(type, listener, options) + onInvalidate(() => document.removeEventListener(type, listener, options)) + }) +} diff --git a/packages/@headlessui-vue/src/hooks/use-outside-click.ts b/packages/@headlessui-vue/src/hooks/use-outside-click.ts index 3d2b748cdb..5083ccf4a9 100644 --- a/packages/@headlessui-vue/src/hooks/use-outside-click.ts +++ b/packages/@headlessui-vue/src/hooks/use-outside-click.ts @@ -1,7 +1,7 @@ -import { useWindowEvent } from './use-window-event' import { computed, Ref, ComputedRef, ref } from 'vue' import { FocusableMode, isFocusableElement } from '../utils/focus-management' import { dom } from '../utils/dom' +import { useDocumentEvent } from './use-document-event' type Container = Ref | HTMLElement | null type ContainerCollection = Container[] | Set @@ -78,7 +78,7 @@ export function useOutsideClick( let initialClickTarget = ref(null) - useWindowEvent( + useDocumentEvent( 'mousedown', (event) => { if (enabled.value) { @@ -88,7 +88,7 @@ export function useOutsideClick( true ) - useWindowEvent( + useDocumentEvent( 'click', (event) => { if (!initialClickTarget.value) { @@ -116,7 +116,7 @@ export function useOutsideClick( // In this case we care only about the first case so we check to see if the active element is the iframe // If so this was because of a click, focus, or other interaction with the child iframe // and we can consider it an "outside click" - useWindowEvent( + useDocumentEvent( 'blur', (event) => handleOutsideClick(event, () => diff --git a/packages/playground-vue/src/components/dialog/dialog.vue b/packages/playground-vue/src/components/dialog/dialog.vue index 6de0eed239..555ddbf051 100644 --- a/packages/playground-vue/src/components/dialog/dialog.vue +++ b/packages/playground-vue/src/components/dialog/dialog.vue @@ -26,7 +26,7 @@ leaveTo="opacity-0" entered="opacity-75" > - +
@@ -186,6 +186,7 @@ import { Dialog, DialogTitle, DialogOverlay, + DialogPanel, Menu, MenuButton, MenuItems, @@ -268,6 +269,7 @@ export default { Dialog, DialogTitle, DialogOverlay, + DialogPanel, Menu, MenuButton, MenuItems,