add Menu examples in Vue
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="flex justify-center w-screen h-full p-12 bg-gray-50">
|
||||
<div class="relative inline-block text-left">
|
||||
<div class="relative inline-block mt-64 text-left">
|
||||
<Menu>
|
||||
<span class="inline-flex rounded-md shadow-sm">
|
||||
<MenuButton
|
||||
@@ -18,40 +18,31 @@
|
||||
</MenuButton>
|
||||
</span>
|
||||
|
||||
<transition
|
||||
enter-active-class="transition-opacity duration-100 ease-out"
|
||||
enter-from-class="transform scale-95 opacity-0"
|
||||
enter-to-class="transform scale-100 opacity-100"
|
||||
leave-active-class="transition-opacity duration-75 ease-out"
|
||||
leave-from-class="transform scale-100 opacity-100"
|
||||
leave-to-class="transform scale-95 opacity-0"
|
||||
<MenuItems
|
||||
ref="popper"
|
||||
class="absolute right-0 w-56 origin-top-right bg-white border border-gray-200 divide-y divide-gray-100 rounded-md shadow-lg outline-none"
|
||||
>
|
||||
<MenuItems
|
||||
ref="popper"
|
||||
class="absolute right-0 w-56 origin-top-right bg-white border border-gray-200 divide-y divide-gray-100 rounded-md shadow-lg outline-none"
|
||||
>
|
||||
<div class="px-4 py-3">
|
||||
<p class="text-sm leading-5">Signed in as</p>
|
||||
<p class="text-sm font-medium leading-5 text-gray-900 truncate">tom@example.com</p>
|
||||
</div>
|
||||
<div class="px-4 py-3">
|
||||
<p class="text-sm leading-5">Signed in as</p>
|
||||
<p class="text-sm font-medium leading-5 text-gray-900 truncate">tom@example.com</p>
|
||||
</div>
|
||||
|
||||
<div class="py-1">
|
||||
<MenuItem as="a" :className="resolveClass" href="#account-settings">
|
||||
Account Settings
|
||||
</MenuItem>
|
||||
<MenuItem v-slot="data">
|
||||
<a href="#support" :class="resolveClass(data)">Support</a>
|
||||
</MenuItem>
|
||||
<MenuItem as="a" :className="resolveClass" disabled href="#new-feature">
|
||||
New feature (soon)
|
||||
</MenuItem>
|
||||
<MenuItem as="a" :className="resolveClass" href="#license">License</MenuItem>
|
||||
</div>
|
||||
<div class="py-1">
|
||||
<MenuItem as="a" :className="resolveClass" href="#sign-out">Sign out</MenuItem>
|
||||
</div>
|
||||
</MenuItems>
|
||||
</transition>
|
||||
<div class="py-1">
|
||||
<MenuItem as="a" :className="resolveClass" href="#account-settings">
|
||||
Account Settings
|
||||
</MenuItem>
|
||||
<MenuItem v-slot="data">
|
||||
<a href="#support" :class="resolveClass(data)">Support</a>
|
||||
</MenuItem>
|
||||
<MenuItem as="a" :className="resolveClass" disabled href="#new-feature">
|
||||
New feature (soon)
|
||||
</MenuItem>
|
||||
<MenuItem as="a" :className="resolveClass" href="#license">License</MenuItem>
|
||||
</div>
|
||||
<div class="py-1">
|
||||
<MenuItem as="a" :className="resolveClass" href="#sign-out">Sign out</MenuItem>
|
||||
</div>
|
||||
</MenuItems>
|
||||
</Menu>
|
||||
</div>
|
||||
</div>
|
||||
@@ -59,35 +50,13 @@
|
||||
|
||||
<script>
|
||||
import { defineComponent, h, ref, onMounted, watchEffect, watch } from 'vue'
|
||||
|
||||
import { createPopper } from '@popperjs/core'
|
||||
import { usePopper } from '../../hooks/use-popper'
|
||||
import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue'
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
function usePopper(options) {
|
||||
const reference = ref(null)
|
||||
const popper = ref(null)
|
||||
|
||||
onMounted(() => {
|
||||
watchEffect(onInvalidate => {
|
||||
const popperEl = popper.value.el || popper.value
|
||||
const referenceEl = reference.value.el || reference.value
|
||||
|
||||
if (!(referenceEl instanceof HTMLElement)) return
|
||||
if (!(popperEl instanceof HTMLElement)) return
|
||||
|
||||
const { destroy } = createPopper(referenceEl, popperEl, options)
|
||||
|
||||
onInvalidate(destroy)
|
||||
})
|
||||
})
|
||||
|
||||
return [reference, popper]
|
||||
}
|
||||
|
||||
export default {
|
||||
components: { Menu, MenuButton, MenuItems, MenuItem },
|
||||
setup(props, context) {
|
||||
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<div class="flex justify-center w-screen h-full p-12 bg-gray-50">
|
||||
<div class="relative inline-block mt-64 text-left">
|
||||
<Menu>
|
||||
<span class="inline-flex rounded-md shadow-sm">
|
||||
<MenuButton
|
||||
ref="reference"
|
||||
class="inline-flex justify-center w-full px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800"
|
||||
>
|
||||
<span>Options</span>
|
||||
<svg class="w-5 h-5 ml-2 -mr-1" 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>
|
||||
|
||||
<div ref="popper" class="w-56">
|
||||
<transition
|
||||
enter-active-class="transition duration-100 ease-out"
|
||||
enter-from-class="transform scale-95 opacity-0"
|
||||
enter-to-class="transform scale-100 opacity-100"
|
||||
leave-active-class="transition duration-75 ease-out"
|
||||
leave-from-class="transform scale-100 opacity-100"
|
||||
leave-to-class="transform scale-95 opacity-0"
|
||||
>
|
||||
<MenuItems
|
||||
class="w-full bg-white border border-gray-200 divide-y divide-gray-100 rounded-md shadow-lg outline-none"
|
||||
>
|
||||
<div class="px-4 py-3">
|
||||
<p class="text-sm leading-5">Signed in as</p>
|
||||
<p class="text-sm font-medium leading-5 text-gray-900 truncate">tom@example.com</p>
|
||||
</div>
|
||||
|
||||
<div class="py-1">
|
||||
<MenuItem as="a" :className="resolveClass" href="#account-settings">
|
||||
Account Settings
|
||||
</MenuItem>
|
||||
<MenuItem v-slot="data">
|
||||
<a href="#support" :class="resolveClass(data)">Support</a>
|
||||
</MenuItem>
|
||||
<MenuItem as="a" :className="resolveClass" disabled href="#new-feature">
|
||||
New feature (soon)
|
||||
</MenuItem>
|
||||
<MenuItem as="a" :className="resolveClass" href="#license">License</MenuItem>
|
||||
</div>
|
||||
<div class="py-1">
|
||||
<MenuItem as="a" :className="resolveClass" href="#sign-out">Sign out</MenuItem>
|
||||
</div>
|
||||
</MenuItems>
|
||||
</transition>
|
||||
</div>
|
||||
</Menu>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, h, ref, onMounted, watchEffect, watch } from 'vue'
|
||||
import { usePopper } from '../../hooks/use-popper'
|
||||
import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue'
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
export default {
|
||||
components: { Menu, MenuButton, MenuItems, MenuItem },
|
||||
setup(props, context) {
|
||||
const [reference, popper] = usePopper({
|
||||
placement: 'bottom-end',
|
||||
strategy: 'fixed',
|
||||
modifiers: [{ name: 'offset', options: { offset: [0, 10] } }],
|
||||
})
|
||||
|
||||
return {
|
||||
reference,
|
||||
popper,
|
||||
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'
|
||||
)
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
+20
-30
@@ -34,13 +34,17 @@
|
||||
</div>
|
||||
|
||||
<div class="py-1">
|
||||
<CustomMenuItem href="#account-settings">Account Settings</CustomMenuItem>
|
||||
<CustomMenuItem href="#support">Support</CustomMenuItem>
|
||||
<CustomMenuItem disabled href="#new-feature">New feature (soon)</CustomMenuItem>
|
||||
<CustomMenuItem href="#license">License</CustomMenuItem>
|
||||
<MenuItem as="a" :className="resolveClass" href="#account-settings">
|
||||
Account Settings
|
||||
</MenuItem>
|
||||
<MenuItem as="a" :className="resolveClass" href="#support">Support</MenuItem>
|
||||
<MenuItem as="a" disabled :className="resolveClass" href="#new-feature">
|
||||
New feature (soon)
|
||||
</MenuItem>
|
||||
<MenuItem as="a" :className="resolveClass" href="#license">License</MenuItem>
|
||||
</div>
|
||||
<div class="py-1">
|
||||
<CustomMenuItem href="#sign-out">Sign out</CustomMenuItem>
|
||||
<MenuItem as="a" :className="resolveClass" href="#sign-out">Sign out</MenuItem>
|
||||
</div>
|
||||
</MenuItems>
|
||||
</transition>
|
||||
@@ -57,37 +61,23 @@ function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
const CustomMenuItem = defineComponent({
|
||||
components: { Menu, MenuButton, MenuItems, MenuItem },
|
||||
setup(props, { slots }) {
|
||||
return () => {
|
||||
return h(MenuItem, ({ active, disabled }) => {
|
||||
return h(
|
||||
'a',
|
||||
{
|
||||
class: 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'
|
||||
),
|
||||
},
|
||||
[
|
||||
h('span', { class: classNames(active && 'font-bold') }, slots.default()),
|
||||
h('kbd', { class: classNames('font-sans', active && 'text-indigo-50') }, '⌘K'),
|
||||
]
|
||||
)
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuItems,
|
||||
MenuItem,
|
||||
CustomMenuItem,
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
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'
|
||||
)
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<div class="flex justify-center w-screen h-full p-12 bg-gray-50">
|
||||
<div class="relative inline-block text-left">
|
||||
<Menu>
|
||||
<span class="rounded-md shadow-sm">
|
||||
<MenuButton
|
||||
class="inline-flex justify-center w-full px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800"
|
||||
>
|
||||
<span>Options</span>
|
||||
<svg class="w-5 h-5 ml-2 -mr-1" 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>
|
||||
|
||||
<MenuItems
|
||||
class="absolute right-0 w-56 mt-2 origin-top-right bg-white border border-gray-200 divide-y divide-gray-100 rounded-md shadow-lg outline-none"
|
||||
>
|
||||
<div class="px-4 py-3">
|
||||
<p class="text-sm leading-5">Signed in as</p>
|
||||
<p class="text-sm font-medium leading-5 text-gray-900 truncate">tom@example.com</p>
|
||||
</div>
|
||||
|
||||
<div class="py-1">
|
||||
<CustomMenuItem href="#account-settings">Account Settings</CustomMenuItem>
|
||||
<CustomMenuItem href="#support">Support</CustomMenuItem>
|
||||
<CustomMenuItem disabled href="#new-feature">New feature (soon)</CustomMenuItem>
|
||||
<CustomMenuItem href="#license">License</CustomMenuItem>
|
||||
</div>
|
||||
<div class="py-1">
|
||||
<CustomMenuItem href="#sign-out">Sign out</CustomMenuItem>
|
||||
</div>
|
||||
</MenuItems>
|
||||
</Menu>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, h } from 'vue'
|
||||
import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue'
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
const CustomMenuItem = defineComponent({
|
||||
components: { Menu, MenuButton, MenuItems, MenuItem },
|
||||
setup(props, { slots }) {
|
||||
return () => {
|
||||
return h(MenuItem, ({ active, disabled }) => {
|
||||
return h(
|
||||
'a',
|
||||
{
|
||||
class: 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'
|
||||
),
|
||||
},
|
||||
[
|
||||
h('span', { class: classNames(active && 'font-bold') }, slots.default()),
|
||||
h('kbd', { class: classNames('font-sans', active && 'text-indigo-50') }, '⌘K'),
|
||||
]
|
||||
)
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuItems,
|
||||
MenuItem,
|
||||
CustomMenuItem,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ref, onMounted, watchEffect } from 'vue'
|
||||
import { createPopper } from '@popperjs/core'
|
||||
|
||||
export function usePopper(options) {
|
||||
const reference = ref(null)
|
||||
const popper = ref(null)
|
||||
|
||||
onMounted(() => {
|
||||
watchEffect(onInvalidate => {
|
||||
const popperEl = popper.value.el || popper.value
|
||||
const referenceEl = reference.value.el || reference.value
|
||||
|
||||
if (!(referenceEl instanceof HTMLElement)) return
|
||||
if (!(popperEl instanceof HTMLElement)) return
|
||||
|
||||
const { destroy } = createPopper(referenceEl, popperEl, options)
|
||||
|
||||
onInvalidate(destroy)
|
||||
})
|
||||
})
|
||||
|
||||
return [reference, popper]
|
||||
}
|
||||
@@ -7,15 +7,25 @@
|
||||
"name": "Menu",
|
||||
"path": "/menu",
|
||||
"children": [
|
||||
{
|
||||
"name": "Menu (basic)",
|
||||
"path": "/menu/menu",
|
||||
"component": "./components/menu/menu.vue"
|
||||
},
|
||||
{
|
||||
"name": "Menu with Popper",
|
||||
"path": "/menu/menu-with-popper",
|
||||
"component": "./components/menu/menu-with-popper.vue"
|
||||
},
|
||||
{
|
||||
"name": "Menu with Tailwind",
|
||||
"path": "/menu/menu-with-tailwind",
|
||||
"component": "./components/menu/menu-with-tailwind.vue"
|
||||
"name": "Menu with Transition",
|
||||
"path": "/menu/menu-with-transition",
|
||||
"component": "./components/menu/menu-with-transition.vue"
|
||||
},
|
||||
{
|
||||
"name": "Menu with Popper + Transition",
|
||||
"path": "/menu/menu-with-transition-and-popper",
|
||||
"component": "./components/menu/menu-with-transition-and-popper.vue"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user