-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Floating UI examples in combination with the Menu component (#2612)
* add Floating UI example for React with Menu * add Floating UI example for Vue with Menu
- Loading branch information
1 parent
1739edb
commit 2eabdd4
Showing
5 changed files
with
988 additions
and
737 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
94 changes: 94 additions & 0 deletions
94
packages/playground-react/pages/menu/menu-with-floating-ui.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import React, { ReactNode, useState, useEffect } from 'react' | ||
import { createPortal } from 'react-dom' | ||
import { Menu } from '@headlessui/react' | ||
import { useFloating, offset } from '@floating-ui/react' | ||
|
||
import { classNames } from '../../utils/class-names' | ||
import { Button } from '../../components/button' | ||
|
||
export default function Home() { | ||
let { refs, floatingStyles } = useFloating({ | ||
placement: 'bottom-end', | ||
strategy: 'fixed', | ||
middleware: [offset(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 ( | ||
<div className="flex h-full w-screen justify-center bg-gray-50 p-12"> | ||
<div className="mt-64 inline-block text-left"> | ||
<Menu> | ||
<span className="inline-flex rounded-md shadow-sm"> | ||
<Menu.Button ref={refs.setReference} as={Button}> | ||
<span>Options</span> | ||
<svg className="ml-2 -mr-1 h-5 w-5" viewBox="0 0 20 20" fill="currentColor"> | ||
<path | ||
fillRule="evenodd" | ||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" | ||
clipRule="evenodd" | ||
/> | ||
</svg> | ||
</Menu.Button> | ||
</span> | ||
|
||
<Portal> | ||
<Menu.Items | ||
className="w-56 divide-y divide-gray-100 rounded-md border border-gray-200 bg-white shadow-lg outline-none" | ||
ref={refs.setFloating} | ||
style={floatingStyles} | ||
> | ||
<div className="px-4 py-3"> | ||
<p className="text-sm leading-5">Signed in as</p> | ||
<p className="truncate text-sm font-medium leading-5 text-gray-900"> | ||
[email protected] | ||
</p> | ||
</div> | ||
|
||
<div className="py-1"> | ||
<Menu.Item as="a" href="#account-settings" className={resolveClass}> | ||
Account settings | ||
</Menu.Item> | ||
<Menu.Item> | ||
{(data) => ( | ||
<a href="#support" className={resolveClass(data)}> | ||
Support | ||
</a> | ||
)} | ||
</Menu.Item> | ||
<Menu.Item as="a" disabled href="#new-feature" className={resolveClass}> | ||
New feature (soon) | ||
</Menu.Item> | ||
<Menu.Item as="a" href="#license" className={resolveClass}> | ||
License | ||
</Menu.Item> | ||
</div> | ||
|
||
<div className="py-1"> | ||
<Menu.Item as="a" href="#sign-out" className={resolveClass}> | ||
Sign out | ||
</Menu.Item> | ||
</div> | ||
</Menu.Items> | ||
</Portal> | ||
</Menu> | ||
</div> | ||
</div> | ||
) | ||
} | ||
|
||
function Portal(props: { children: ReactNode }) { | ||
let { children } = props | ||
let [mounted, setMounted] = useState(false) | ||
|
||
useEffect(() => setMounted(true), []) | ||
|
||
if (!mounted) return null | ||
return createPortal(children, document.body) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
95 changes: 95 additions & 0 deletions
95
packages/playground-vue/src/components/menu/menu-with-floating-ui.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
<template> | ||
<div class="flex h-full w-screen justify-center bg-gray-50 p-12"> | ||
<div class="mt-64 inline-block text-left"> | ||
<Menu> | ||
<span class="inline-flex rounded-md shadow-sm"> | ||
<MenuButton | ||
ref="reference" | ||
class="focus:shadow-outline-blue inline-flex w-full justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out hover:text-gray-500 focus:border-blue-300 focus:outline-none active:bg-gray-50 active:text-gray-800" | ||
> | ||
<span>Options</span> | ||
<svg class="ml-2 -mr-1 h-5 w-5" viewBox="0 0 20 20" fill="currentColor"> | ||
<path | ||
fillRule="evenodd" | ||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" | ||
clipRule="evenodd" | ||
/> | ||
</svg> | ||
</MenuButton> | ||
</span> | ||
|
||
<teleport to="body"> | ||
<MenuItems | ||
ref="floating" | ||
:style="floatingStyles" | ||
class="absolute right-0 w-56 origin-top-right divide-y divide-gray-100 rounded-md border border-gray-200 bg-white shadow-lg outline-none" | ||
> | ||
<div class="px-4 py-3"> | ||
<p class="text-sm leading-5">Signed in as</p> | ||
<p class="truncate text-sm font-medium leading-5 text-gray-900">[email protected]</p> | ||
</div> | ||
|
||
<div class="py-1"> | ||
<MenuItem as="a" href="#account-settings" :className="resolveClass"> | ||
Account settings | ||
</MenuItem> | ||
<MenuItem v-slot="data"> | ||
<a href="#support" :class="resolveClass(data)">Support</a> | ||
</MenuItem> | ||
<MenuItem as="a" disabled href="#new-feature" :className="resolveClass"> | ||
New feature (soon) | ||
</MenuItem> | ||
<MenuItem as="a" href="#license" :className="resolveClass">License</MenuItem> | ||
</div> | ||
<div class="py-1"> | ||
<MenuItem as="a" href="#sign-out" :className="resolveClass">Sign out</MenuItem> | ||
</div> | ||
</MenuItems> | ||
</teleport> | ||
</Menu> | ||
</div> | ||
</div> | ||
</template> | ||
|
||
<script> | ||
import { defineComponent, h, ref, onMounted, watchEffect, watch, computed } from 'vue' | ||
import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue' | ||
import { useFloating, offset } from '@floating-ui/vue' | ||
function classNames(...classes) { | ||
return classes.filter(Boolean).join(' ') | ||
} | ||
export default { | ||
components: { Menu, MenuButton, MenuItems, MenuItem }, | ||
setup(props, context) { | ||
let reference = ref(null) | ||
let floating = ref(null) | ||
let { floatingStyles } = useFloating( | ||
computed(() => reference.value?.el), | ||
computed(() => floating.value?.el), | ||
{ | ||
placement: 'bottom-end', | ||
strategy: 'fixed', | ||
middleware: [offset(10)], | ||
} | ||
) | ||
function resolveClass({ active, disabled }) { | ||
return classNames( | ||
'flex justify-between w-full px-4 py-2 text-sm leading-5 text-left', | ||
active ? 'bg-gray-100 text-gray-900' : 'text-gray-700', | ||
disabled && 'cursor-not-allowed opacity-50' | ||
) | ||
} | ||
return { | ||
reference, | ||
floating, | ||
floatingStyles, | ||
resolveClass, | ||
} | ||
}, | ||
} | ||
</script> |
Oops, something went wrong.
2eabdd4
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
headlessui-vue – ./packages/playground-vue
headlessui-vue.vercel.app
headlessui-vue-git-main-tailwindlabs.vercel.app
headlessui-vue-tailwindlabs.vercel.app
2eabdd4
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
headlessui-react – ./packages/playground-react
headlessui-react-git-main-tailwindlabs.vercel.app
headlessui-react.vercel.app
headlessui-react-tailwindlabs.vercel.app