chore: update components

This commit is contained in:
Benjamin Canac
2021-11-22 17:29:54 +01:00
parent e76b4b2951
commit 199c466e62
4 changed files with 82 additions and 232 deletions

View File

@@ -6,8 +6,9 @@
</slot>
</MenuButton>
<div ref="container" :class="containerClass">
<div v-if="open" ref="container" :class="containerClass">
<transition
appear
enter-active-class="transition duration-100 ease-out"
enter-from-class="transform scale-95 opacity-0"
enter-to-class="transform scale-100 opacity-100"
@@ -15,7 +16,7 @@
leave-from-class="transform scale-100 opacity-100"
leave-to-class="transform scale-95 opacity-0"
>
<MenuItems :class="itemsClass">
<MenuItems :class="itemsClass" static>
<div v-for="(subItems, index) of items" :key="index" class="py-1">
<MenuItem v-for="(item, subIndex) of subItems" :key="subIndex" v-slot="{ active, disabled }">
<Component v-bind="item" :is="(item.to && 'NuxtLink') || (item.href && 'a') || 'button'" :class="resolveItemClass({ active, disabled })" @click="onItemClick(item)">
@@ -97,14 +98,6 @@ export default {
type: String,
default: 'mr-3 h-5 w-5 text-tw-gray-400 group-hover:text-tw-gray-500'
}
// openDelay: {
// type: Number,
// default: 100
// },
// closeDelay: {
// type: Number,
// default: 0
// },
},
setup (props) {
const [trigger, container] = usePopper({

View File

@@ -19,8 +19,8 @@
class="flex items-center px-2 py-2 text-sm font-medium rounded-md group focus:outline-none"
:active-class="activeVariantClass"
:inactive-class="variantClass"
@click.native="link.click && link.click()"
@keyup.enter.native="$event.target.blur()"
@click="link.click && link.click()"
@keyup.enter="$event.target.blur()"
>
<slot name="icon" :link="link">
<Icon

View File

@@ -7,54 +7,34 @@
as="template"
enter="ease-out duration-300"
enter-from="opacity-0"
enter-to="opacity-75"
enter-to="opacity-100"
leave="ease-in duration-200"
leave-from="opacity-75"
leave-from="opacity-100"
leave-to="opacity-0"
entered="opacity-75"
>
<DialogOverlay class="fixed inset-0 bg-gray-500 transition-opacity" />
<DialogOverlay class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
</TransitionChild>
<!-- This element is to trick the browser into centering the modal contents. -->
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">&#8203;</span>
<TransitionChild
enter="ease-out transform duration-300"
as="template"
enter="ease-out duration-300"
enter-from="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
enter-to="opacity-100 translate-y-0 sm:scale-100"
leave="ease-in transform duration-200"
leave="ease-in duration-200"
leave-from="opacity-100 translate-y-0 sm:scale-100"
leave-to="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<!-- This element is to trick the browser into centering the modal contents. -->
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">
&#8203;
</span>
<Card
class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full"
v-bind="$attrs"
variant="white"
ring-class="sm:ring-1 sm:ring-transparent dark:ring-gray-700"
>
<template v-if="$slots.header" #header>
<slot name="header" />
</template>
<template v-else-if="title" #header>
<div class="flex items-center justify-between">
<h2 class="font-medium sm:text-lg sm:leading-6 text-tw-gray-900">
{{ title }}
</h2>
<div class="flex items-center">
<button
type="button"
aria-label="Close panel"
class="rounded-md text-tw-gray-400 hover:text-tw-gray-500 focus:outline-none focus:ring-2 focus:ring-primary-500"
@click="isOpen = false"
>
<Icon name="heroicons-outline:x" class="w-6 h-6" />
</button>
</div>
</div>
</template>
<slot />
<template v-if="$slots.footer" #footer>
<slot name="footer" />
@@ -71,7 +51,6 @@
import { Dialog, DialogOverlay, TransitionRoot, TransitionChild } from '@headlessui/vue'
import Card from '../layout/Card'
import Icon from '../elements/Icon'
export default {
components: {
@@ -79,8 +58,7 @@ export default {
DialogOverlay,
TransitionRoot,
TransitionChild,
Card,
Icon
Card
},
props: {
modelValue: {

View File

@@ -1,211 +1,90 @@
<template>
<div
ref="container"
class="inline whitespace-normal"
@mouseover="mode === 'hover' ? mouseover() : () => {}"
@mouseleave="mode === 'hover' ? mouseleave() : () => {}"
>
<slot :toggle="toggle" />
<Popover v-slot="{ open }" :class="wrapperClass">
<PopoverButton ref="trigger" as="div">
<slot :open="open">
Open
</slot>
</PopoverButton>
<Portal to="popover">
<div v-if="open" ref="container" :class="containerClass">
<transition
enter-class="transform scale-95 opacity-0"
enter-active-class="transition duration-100 ease-out"
enter-to-class="transform scale-100 opacity-100"
leave-class="opacity-100"
leave-active-class="duration-100 ease-in"
leave-to-class="opacity-0"
appear
enter-active-class="transition ease-out duration-200"
enter-from-class="opacity-0 translate-y-1"
enter-to-class="opacity-100 translate-y-0"
leave-active-class="transition ease-in duration-150"
leave-from-class="opacity-100 translate-y-0"
leave-to-class="opacity-0 translate-y-1"
>
<div
v-if="show && ready && $slots.content"
ref="popover"
class="z-30 flex bg-white rounded-md shadow ring-1 ring-gray-200 dark:ring-gray-700"
:class="[
popoverClass,
darken ? 'dark:bg-gray-900' : 'dark:bg-gray-800',
mode === 'click' ? '' : 'invisible lg:visible'
]"
@mouseover="mode === 'hover' ? mouseover() : () => {}"
@mouseleave="mode === 'hover' ? mouseleave() : () => {}"
>
<slot name="content" :toggle="toggle" />
</div>
<PopoverPanel :class="panelClass" static>
<slot name="panel" />
</PopoverPanel>
</transition>
</Portal>
</div>
</div>
</Popover>
</template>
<script>
import { createPopper } from '@popperjs/core'
// import { directive as onClickaway } from 'vue-clickaway'
import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'
import { usePopper } from '../../utils'
export default {
// directives: {
// onClickaway
// },
components: {
Popover,
PopoverButton,
PopoverPanel
},
props: {
darken: {
type: Boolean,
default: false
},
popoverClass: {
type: String,
default: 'w-auto h-auto'
},
mode: {
type: String,
default: 'hover',
validator (value) {
return ['click', 'hover'].includes(value)
}
},
strategy: {
type: String,
default: 'fixed'
},
placement: {
type: String,
default: 'bottom'
},
openDelay: {
type: Number,
default: 300
strategy: {
type: String,
default: 'absolute'
},
closeDelay: {
type: Number,
default: 100
wrapperClass: {
type: String,
default: 'relative'
},
ready: {
type: Boolean,
default: true
containerClass: {
type: String,
default: 'z-10'
},
panelClass: {
type: String,
default: 'transform'
}
},
data () {
setup (props) {
const [trigger, container] = usePopper({
placement: props.placement,
strategy: props.strategy,
modifiers: [{
name: 'offset',
options: {
offset: [0, 8]
}
},
{
name: 'computeStyles',
options: {
gpuAcceleration: false,
adaptive: false
}
},
{
name: 'preventOverflow',
options: {
padding: 8
}
}]
})
return {
show: false,
instance: null,
openTimeout: null,
closeTimeout: null
}
},
watch: {
show (value) {
this.$emit('show', value)
if (!value || !this.ready) {
// let the 100ms hide animation proceed before destroying the popper instance
setTimeout(() => {
// if popover reshow, abort destroy
if (this.show && this.ready) {
return
}
this.destroyPopper()
}, 120)
return
}
this.createPopper()
},
ready (value) {
if (!value || !this.show) {
// let the 100ms hide animation proceed before destroying the popper instance
setTimeout(() => {
// if popover reshow, abort destroy
if (this.show && this.ready) {
return
}
this.destroyPopper()
}, 120)
return
}
this.createPopper()
}
},
beforeDestroy () {
this.destroyPopper()
},
methods: {
createPopper () {
// https://portal-vue.linusb.org/guide/caveats.html#refs
this.$nextTick().then(
this.$nextTick().then(() => {
setTimeout(() => {
if (this.instance) {
this.instance.forceUpdate()
return
}
this.instance = createPopper(this.$refs.container, this.$refs.popover, {
strategy: this.strategy,
placement: this.placement,
modifiers: [
{
name: 'offset',
options: {
offset: [0, 8]
}
},
{
name: 'computeStyles',
options: {
gpuAcceleration: false,
adaptive: false
}
},
{
name: 'preventOverflow',
options: {
padding: 8
}
}
]
})
}, 1)
})
)
},
destroyPopper () {
if (this.instance) {
this.instance.destroy()
this.instance = null
}
},
mouseover () {
clearTimeout(this.closeTimeout)
this.closeTimeout = null
if (this.show) {
return
}
this.openTimeout = this.openTimeout || setTimeout(() => {
this.open()
this.openTimeout = null
}, this.openDelay)
},
mouseleave () {
clearTimeout(this.openTimeout)
this.openTimeout = null
if (!this.show) {
return
}
this.closeTimeout = this.closeTimeout || setTimeout(() => {
this.close()
this.closeTimeout = null
}, this.closeDelay)
},
toggle () {
this.show = !this.show
},
open () {
this.show = true
},
close () {
this.show = false
},
onClickaway () {
if (this.mode !== 'hover') {
this.close()
}
trigger,
container
}
}
}