diff --git a/packages/@headlessui-react/CHANGELOG.md b/packages/@headlessui-react/CHANGELOG.md index aaa769a..0c748df 100644 --- a/packages/@headlessui-react/CHANGELOG.md +++ b/packages/@headlessui-react/CHANGELOG.md @@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `by` prop for `Listbox`, `Combobox` and `RadioGroup` ([#1482](https://github.com/tailwindlabs/headlessui/pull/1482)) - Add `@headlessui/tailwindcss` plugin ([#1487](https://github.com/tailwindlabs/headlessui/pull/1487)) +### Fixed + +- Fixed SSR support on Deno ([#1671](https://github.com/tailwindlabs/headlessui/pull/1671)) + ## [1.6.6] - 2022-07-07 ### Fixed diff --git a/packages/@headlessui-react/src/components/portal/portal.tsx b/packages/@headlessui-react/src/components/portal/portal.tsx index 86bdba1..614a80a 100644 --- a/packages/@headlessui-react/src/components/portal/portal.tsx +++ b/packages/@headlessui-react/src/components/portal/portal.tsx @@ -21,6 +21,7 @@ import { useServerHandoffComplete } from '../../hooks/use-server-handoff-complet import { optionalRef, useSyncRefs } from '../../hooks/use-sync-refs' import { useOwnerDocument } from '../../hooks/use-owner' import { microTask } from '../../utils/micro-task' +import { isServer } from '../../utils/ssr' function usePortalTarget(ref: MutableRefObject): HTMLElement | null { let forceInRoot = usePortalRoot() @@ -33,7 +34,7 @@ function usePortalTarget(ref: MutableRefObject): HTMLElement if (!forceInRoot && groupTarget !== null) return null // No group context is used, let's create a default portal root - if (typeof window === 'undefined') return null + if (isServer) return null let existingRoot = ownerDocument?.getElementById('headlessui-portal-root') if (existingRoot) return existingRoot @@ -81,7 +82,7 @@ let PortalRoot = forwardRefWithAs(function Portal< let ownerDocument = useOwnerDocument(internalPortalRootRef) let target = usePortalTarget(internalPortalRootRef) let [element] = useState(() => - typeof window === 'undefined' ? null : ownerDocument?.createElement('div') ?? null + isServer ? null : ownerDocument?.createElement('div') ?? null ) let ready = useServerHandoffComplete() diff --git a/packages/@headlessui-react/src/hooks/use-iso-morphic-effect.ts b/packages/@headlessui-react/src/hooks/use-iso-morphic-effect.ts index 7c7eae2..79a2219 100644 --- a/packages/@headlessui-react/src/hooks/use-iso-morphic-effect.ts +++ b/packages/@headlessui-react/src/hooks/use-iso-morphic-effect.ts @@ -1,3 +1,4 @@ import { useLayoutEffect, useEffect } from 'react' +import { isServer } from '../utils/ssr' -export let useIsoMorphicEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect +export let useIsoMorphicEffect = isServer ? useEffect : useLayoutEffect diff --git a/packages/@headlessui-react/src/utils/owner.ts b/packages/@headlessui-react/src/utils/owner.ts index 296975e..5bbc186 100644 --- a/packages/@headlessui-react/src/utils/owner.ts +++ b/packages/@headlessui-react/src/utils/owner.ts @@ -1,9 +1,10 @@ import { MutableRefObject } from 'react' +import { isServer } from './ssr' export function getOwnerDocument>( element: T | null | undefined ) { - if (typeof window === 'undefined') return null + if (isServer) return null if (element instanceof Node) return element.ownerDocument if (element?.hasOwnProperty('current')) { if (element.current instanceof Node) return element.current.ownerDocument diff --git a/packages/@headlessui-react/src/utils/ssr.ts b/packages/@headlessui-react/src/utils/ssr.ts new file mode 100644 index 0000000..5df1767 --- /dev/null +++ b/packages/@headlessui-react/src/utils/ssr.ts @@ -0,0 +1 @@ +export const isServer = typeof window === 'undefined' || typeof document === 'undefined' diff --git a/packages/@headlessui-vue/CHANGELOG.md b/packages/@headlessui-vue/CHANGELOG.md index dfcf8ba..7638e9d 100644 --- a/packages/@headlessui-vue/CHANGELOG.md +++ b/packages/@headlessui-vue/CHANGELOG.md @@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `by` prop for `Listbox`, `Combobox` and `RadioGroup` ([#1482](https://github.com/tailwindlabs/headlessui/pull/1482)) - Add `@headlessui/tailwindcss` plugin ([#1487](https://github.com/tailwindlabs/headlessui/pull/1487)) +### Fixed + +- Fixed SSR support on Deno ([#1671](https://github.com/tailwindlabs/headlessui/pull/1671)) + ## [1.6.7] - 2022-07-12 ### Fixed diff --git a/packages/@headlessui-vue/src/components/focus-trap/focus-trap.test.ts b/packages/@headlessui-vue/src/components/focus-trap/focus-trap.test.ts index 16ed5f3..8ae1d20 100644 --- a/packages/@headlessui-vue/src/components/focus-trap/focus-trap.test.ts +++ b/packages/@headlessui-vue/src/components/focus-trap/focus-trap.test.ts @@ -1,4 +1,4 @@ -import { ref, nextTick, onMounted } from 'vue' +import { ref, onMounted } from 'vue' import { FocusTrap } from './focus-trap' import { assertActiveElement, getByText } from '../../test-utils/accessibility-assertions' diff --git a/packages/@headlessui-vue/src/hooks/use-event-listener.ts b/packages/@headlessui-vue/src/hooks/use-event-listener.ts index 7f761a3..07a507e 100644 --- a/packages/@headlessui-vue/src/hooks/use-event-listener.ts +++ b/packages/@headlessui-vue/src/hooks/use-event-listener.ts @@ -1,4 +1,5 @@ import { watchEffect } from 'vue' +import { isServer } from '../utils/ssr' export function useEventListener( element: HTMLElement | Document | Window | EventTarget | null | undefined, @@ -6,7 +7,7 @@ export function useEventListener( listener: (event: WindowEventMap[TType]) => any, options?: boolean | AddEventListenerOptions ) { - if (typeof window === 'undefined') return + if (isServer) return watchEffect((onInvalidate) => { element = element ?? window diff --git a/packages/@headlessui-vue/src/hooks/use-window-event.ts b/packages/@headlessui-vue/src/hooks/use-window-event.ts index 5c9f971..267d78b 100644 --- a/packages/@headlessui-vue/src/hooks/use-window-event.ts +++ b/packages/@headlessui-vue/src/hooks/use-window-event.ts @@ -1,11 +1,12 @@ import { watchEffect } from 'vue' +import { isServer } from '../utils/ssr' export function useWindowEvent( type: TType, listener: (this: Window, ev: WindowEventMap[TType]) => any, options?: boolean | AddEventListenerOptions ) { - if (typeof window === 'undefined') return + if (isServer) return watchEffect((onInvalidate) => { window.addEventListener(type, listener, options) diff --git a/packages/@headlessui-vue/src/utils/owner.ts b/packages/@headlessui-vue/src/utils/owner.ts index afbe9d7..eebd7c6 100644 --- a/packages/@headlessui-vue/src/utils/owner.ts +++ b/packages/@headlessui-vue/src/utils/owner.ts @@ -1,10 +1,11 @@ import { Ref } from 'vue' import { dom } from './dom' +import { isServer } from './ssr' export function getOwnerDocument>( element: T | null | undefined ) { - if (typeof window === 'undefined') return null + if (isServer) return null if (element instanceof Node) return element.ownerDocument if (element?.hasOwnProperty('value')) { let domElement = dom(element) diff --git a/packages/@headlessui-vue/src/utils/ssr.ts b/packages/@headlessui-vue/src/utils/ssr.ts new file mode 100644 index 0000000..5df1767 --- /dev/null +++ b/packages/@headlessui-vue/src/utils/ssr.ts @@ -0,0 +1 @@ +export const isServer = typeof window === 'undefined' || typeof document === 'undefined' diff --git a/packages/playground-react/pages/combinations/form.tsx b/packages/playground-react/pages/combinations/form.tsx index 3c8fcb5..99bc2f4 100644 --- a/packages/playground-react/pages/combinations/form.tsx +++ b/packages/playground-react/pages/combinations/form.tsx @@ -20,7 +20,9 @@ let people = [ let locations = ['New York', 'London', 'Paris', 'Berlin'] export default function App() { - let [result, setResult] = useState(() => (typeof window === 'undefined' ? [] : new FormData())) + let [result, setResult] = useState(() => + typeof window === 'undefined' || typeof document === 'undefined' ? [] : new FormData() + ) let [notifications, setNotifications] = useState(false) let [apple, setApple] = useState(false) let [banana, setBanana] = useState(false)