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> </slot>
</MenuButton> </MenuButton>
<div ref="container" :class="containerClass"> <div v-if="open" ref="container" :class="containerClass">
<transition <transition
appear
enter-active-class="transition duration-100 ease-out" enter-active-class="transition duration-100 ease-out"
enter-from-class="transform scale-95 opacity-0" enter-from-class="transform scale-95 opacity-0"
enter-to-class="transform scale-100 opacity-100" enter-to-class="transform scale-100 opacity-100"
@@ -15,7 +16,7 @@
leave-from-class="transform scale-100 opacity-100" leave-from-class="transform scale-100 opacity-100"
leave-to-class="transform scale-95 opacity-0" 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"> <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 }"> <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)"> <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, type: String,
default: 'mr-3 h-5 w-5 text-tw-gray-400 group-hover:text-tw-gray-500' 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) { setup (props) {
const [trigger, container] = usePopper({ 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" class="flex items-center px-2 py-2 text-sm font-medium rounded-md group focus:outline-none"
:active-class="activeVariantClass" :active-class="activeVariantClass"
:inactive-class="variantClass" :inactive-class="variantClass"
@click.native="link.click && link.click()" @click="link.click && link.click()"
@keyup.enter.native="$event.target.blur()" @keyup.enter="$event.target.blur()"
> >
<slot name="icon" :link="link"> <slot name="icon" :link="link">
<Icon <Icon

View File

@@ -7,54 +7,34 @@
as="template" as="template"
enter="ease-out duration-300" enter="ease-out duration-300"
enter-from="opacity-0" enter-from="opacity-0"
enter-to="opacity-75" enter-to="opacity-100"
leave="ease-in duration-200" leave="ease-in duration-200"
leave-from="opacity-75" leave-from="opacity-100"
leave-to="opacity-0" 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> </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 <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-from="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
enter-to="opacity-100 translate-y-0 sm:scale-100" 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-from="opacity-100 translate-y-0 sm:scale-100"
leave-to="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" 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 <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" 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" v-bind="$attrs"
variant="white"
ring-class="sm:ring-1 sm:ring-transparent dark:ring-gray-700" ring-class="sm:ring-1 sm:ring-transparent dark:ring-gray-700"
> >
<template v-if="$slots.header" #header> <template v-if="$slots.header" #header>
<slot name="header" /> <slot name="header" />
</template> </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 /> <slot />
<template v-if="$slots.footer" #footer> <template v-if="$slots.footer" #footer>
<slot name="footer" /> <slot name="footer" />
@@ -71,7 +51,6 @@
import { Dialog, DialogOverlay, TransitionRoot, TransitionChild } from '@headlessui/vue' import { Dialog, DialogOverlay, TransitionRoot, TransitionChild } from '@headlessui/vue'
import Card from '../layout/Card' import Card from '../layout/Card'
import Icon from '../elements/Icon'
export default { export default {
components: { components: {
@@ -79,8 +58,7 @@ export default {
DialogOverlay, DialogOverlay,
TransitionRoot, TransitionRoot,
TransitionChild, TransitionChild,
Card, Card
Icon
}, },
props: { props: {
modelValue: { modelValue: {

View File

@@ -1,211 +1,90 @@
<template> <template>
<div <Popover v-slot="{ open }" :class="wrapperClass">
ref="container" <PopoverButton ref="trigger" as="div">
class="inline whitespace-normal" <slot :open="open">
@mouseover="mode === 'hover' ? mouseover() : () => {}" Open
@mouseleave="mode === 'hover' ? mouseleave() : () => {}" </slot>
> </PopoverButton>
<slot :toggle="toggle" />
<Portal to="popover"> <div v-if="open" ref="container" :class="containerClass">
<transition <transition
enter-class="transform scale-95 opacity-0" appear
enter-active-class="transition duration-100 ease-out" enter-active-class="transition ease-out duration-200"
enter-to-class="transform scale-100 opacity-100" enter-from-class="opacity-0 translate-y-1"
leave-class="opacity-100" enter-to-class="opacity-100 translate-y-0"
leave-active-class="duration-100 ease-in" leave-active-class="transition ease-in duration-150"
leave-to-class="opacity-0" leave-from-class="opacity-100 translate-y-0"
leave-to-class="opacity-0 translate-y-1"
> >
<div <PopoverPanel :class="panelClass" static>
v-if="show && ready && $slots.content" <slot name="panel" />
ref="popover" </PopoverPanel>
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>
</transition> </transition>
</Portal> </div>
</div> </Popover>
</template> </template>
<script> <script>
import { createPopper } from '@popperjs/core' import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'
// import { directive as onClickaway } from 'vue-clickaway'
import { usePopper } from '../../utils'
export default { export default {
// directives: { components: {
// onClickaway Popover,
// }, PopoverButton,
PopoverPanel
},
props: { 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: { placement: {
type: String, type: String,
default: 'bottom' default: 'bottom'
}, },
openDelay: { strategy: {
type: Number, type: String,
default: 300 default: 'absolute'
}, },
closeDelay: { wrapperClass: {
type: Number, type: String,
default: 100 default: 'relative'
}, },
ready: { containerClass: {
type: Boolean, type: String,
default: true 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 { return {
show: false, trigger,
instance: null, container
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()
}
} }
} }
} }