diff --git a/packages/@headlessui-react/pages/menu/menu-with-popper.tsx b/packages/@headlessui-react/pages/menu/menu-with-popper.tsx index 73c996b..4629720 100644 --- a/packages/@headlessui-react/pages/menu/menu-with-popper.tsx +++ b/packages/@headlessui-react/pages/menu/menu-with-popper.tsx @@ -2,11 +2,11 @@ import * as React from 'react' import * as ReactDOM from 'react-dom' import Head from 'next/head' import Link from 'next/link' -import { createPopper, Options } from '@popperjs/core' import { Menu } from '@headlessui/react' import { classNames } from '../../src/utils/class-names' import { PropsOf } from '../../src/types' +import { usePopper } from '../../playground-utils/hooks/use-popper' export default function Home() { return ( @@ -22,41 +22,6 @@ export default function Home() { ) } -/** - * Example implementation to use Popper: https://popper.js.org/ - */ -function usePopper( - options?: Partial -): [React.RefCallback, React.RefCallback] { - const reference = React.useRef(null) - const popper = React.useRef(null) - - const cleanupCallback = React.useRef(() => {}) - - const instantiatePopper = React.useCallback(() => { - if (!reference.current) return - if (!popper.current) return - - if (cleanupCallback.current) cleanupCallback.current() - - cleanupCallback.current = createPopper(reference.current, popper.current, options).destroy - }, [reference, popper, cleanupCallback, options]) - - return React.useMemo( - () => [ - referenceDomNode => { - reference.current = referenceDomNode - instantiatePopper() - }, - popperDomNode => { - popper.current = popperDomNode - instantiatePopper() - }, - ], - [reference, popper, instantiatePopper] - ) -} - function Portal(props: { children: React.ReactNode }) { const { children } = props const [mounted, setMounted] = React.useState(false) @@ -74,6 +39,14 @@ function Dropdown() { modifiers: [{ name: 'offset', options: { offset: [0, 10] } }], }) + function resolveClass({ active, disabled }) { + return classNames( + 'block w-full text-left px-4 py-2 text-sm leading-5 text-gray-700', + active && 'bg-gray-100 text-gray-900', + disabled && 'cursor-not-allowed opacity-50' + ) + } + return (
@@ -95,12 +68,6 @@ function Dropdown() { @@ -112,18 +79,22 @@ function Dropdown() {
- Account settings - + + Account settings + + Support - - + + New feature (soon) - - License + + + License +
- +
@@ -158,19 +129,3 @@ function SignOutButton(props) { ) } - -function Item(props: PropsOf) { - return ( - - classNames( - 'block w-full text-left px-4 py-2 text-sm leading-5 text-gray-700', - active && 'bg-gray-100 text-gray-900', - disabled && 'cursor-not-allowed opacity-50' - ) - } - {...props} - /> - ) -} diff --git a/packages/@headlessui-react/pages/menu/menu-with-transition-and-popper.tsx b/packages/@headlessui-react/pages/menu/menu-with-transition-and-popper.tsx new file mode 100644 index 0000000..7da8fb5 --- /dev/null +++ b/packages/@headlessui-react/pages/menu/menu-with-transition-and-popper.tsx @@ -0,0 +1,162 @@ +import * as React from 'react' +import Head from 'next/head' +import Link from 'next/link' +import { Menu } from '@headlessui/react' + +import { classNames } from '../../src/utils/class-names' +import { PropsOf } from '../../src/types' +import { usePopper } from '../../playground-utils/hooks/use-popper' + +export default function Home() { + return ( + <> + + Menu with pure Tailwind- Playground + + +
+ +
+ + ) +} + +function Dropdown() { + const [trigger, container] = usePopper({ + placement: 'bottom-end', + strategy: 'fixed', + modifiers: [{ name: 'offset', options: { offset: [0, 10] } }], + }) + + return ( +
+ + + + {({ open }) => ( + <> + Options + + + + + )} + + + +
+ +
+

Signed in as

+

+ tom@example.com +

+
+ +
+ Account settings + + Support + + + New feature (soon) + + License +
+ +
+ +
+
+
+
+
+ ) +} + +function NextLink(props: PropsOf<'a'>) { + const { href, children, ...rest } = props + return ( + + {children} + + ) +} + +function SignOutButton() { + return ( + + {props => { + const { active, disabled } = props + return ( +
{ + e.preventDefault() + alert('SIGNED OUT') + }} + > + +
+ ) + }} +
+ ) +} + +function Item({ children, as = 'a', ...props }: PropsOf) { + return ( + { + return classNames( + 'flex justify-between w-full text-left px-4 py-2 text-sm leading-5', + active ? 'bg-indigo-500 text-white' : 'text-gray-700', + disabled && 'cursor-not-allowed opacity-50' + ) + }} + as={as} + {...props} + > + {({ active }) => ( + <> + {children} + ⌘K + + )} + + ) +} diff --git a/packages/@headlessui-react/pages/menu/menu-with-pure-tailwind.tsx b/packages/@headlessui-react/pages/menu/menu-with-transition.tsx similarity index 96% rename from packages/@headlessui-react/pages/menu/menu-with-pure-tailwind.tsx rename to packages/@headlessui-react/pages/menu/menu-with-transition.tsx index 848e01d..4040677 100644 --- a/packages/@headlessui-react/pages/menu/menu-with-pure-tailwind.tsx +++ b/packages/@headlessui-react/pages/menu/menu-with-transition.tsx @@ -27,7 +27,7 @@ function Dropdown() { {({ open }) => ( - + )} @@ -124,7 +124,7 @@ function SignOutButton() { ) } -function Item({ children, href, ...props }: PropsOf) { +function Item({ children, as = 'a', ...props }: PropsOf) { return ( { @@ -134,13 +134,14 @@ function Item({ children, href, ...props }: PropsOf) { disabled && 'cursor-not-allowed opacity-50' ) }} + as={as} {...props} > {({ active }) => ( - + <> {children} ⌘K - + )} ) diff --git a/packages/@headlessui-react/pages/menu/menu.tsx b/packages/@headlessui-react/pages/menu/menu.tsx new file mode 100644 index 0000000..424546c --- /dev/null +++ b/packages/@headlessui-react/pages/menu/menu.tsx @@ -0,0 +1,122 @@ +import * as React from 'react' +import Head from 'next/head' +import Link from 'next/link' +import { Menu } from '@headlessui/react' + +import { classNames } from '../../src/utils/class-names' +import { PropsOf } from '../../src/types' + +export default function Home() { + return ( + <> + + Menu with pure Tailwind- Playground + + +
+ +
+ + ) +} + +function Dropdown() { + function resolveClass({ active, disabled }) { + return classNames( + 'flex justify-between w-full text-left px-4 py-2 text-sm leading-5', + active ? 'bg-indigo-500 text-white' : 'text-gray-700', + disabled && 'cursor-not-allowed opacity-50' + ) + } + + return ( +
+ + + + Options + + + + + + + +
+

Signed in as

+

tom@example.com

+
+ +
+ + Account settings + + + Support + + + New feature (soon) + + + License + +
+ +
+ +
+
+
+
+ ) +} + +function NextLink(props: PropsOf<'a'>) { + const { href, children, ...rest } = props + return ( + + {children} + + ) +} + +function SignOutButton() { + return ( + + {props => { + const { active, disabled } = props + return ( +
{ + e.preventDefault() + alert('SIGNED OUT') + }} + > + +
+ ) + }} +
+ ) +} diff --git a/packages/@headlessui-react/playground-utils/hooks/use-popper.ts b/packages/@headlessui-react/playground-utils/hooks/use-popper.ts new file mode 100644 index 0000000..ddd9445 --- /dev/null +++ b/packages/@headlessui-react/playground-utils/hooks/use-popper.ts @@ -0,0 +1,37 @@ +import React from 'react' +import { createPopper, Options } from '@popperjs/core' + +/** + * Example implementation to use Popper: https://popper.js.org/ + */ +export function usePopper( + options?: Partial +): [React.RefCallback, React.RefCallback] { + const reference = React.useRef(null) + const popper = React.useRef(null) + + const cleanupCallback = React.useRef(() => {}) + + const instantiatePopper = React.useCallback(() => { + if (!reference.current) return + if (!popper.current) return + + if (cleanupCallback.current) cleanupCallback.current() + + cleanupCallback.current = createPopper(reference.current, popper.current, options).destroy + }, [reference, popper, cleanupCallback, options]) + + return React.useMemo( + () => [ + referenceDomNode => { + reference.current = referenceDomNode + instantiatePopper() + }, + popperDomNode => { + popper.current = popperDomNode + instantiatePopper() + }, + ], + [reference, popper, instantiatePopper] + ) +}