Last active
July 24, 2020 16:49
-
-
Save james2doyle/9ae0e74ab45d7e80d1953a3c7d8e0a1d to your computer and use it in GitHub Desktop.
A popover component for Vue.js
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
<!-- | |
Popover Component | |
Usage: | |
<popover class="ml-3 inline-block align-text-top" v-cloak> | |
<template #icon> | |
<div class="w-5 icon-plus"></div> | |
</template> | |
<template #content> | |
<strong>Title</strong> | |
<p>Some content is here</p> | |
</template> | |
</popover> | |
--> | |
<template> | |
<div ref="popover" class="popover" @mouseenter="onEnter" @mouseleave="onLeave" @touchstart="onEnter"> | |
<slot name="icon"> | |
<strong>Icon slot required</strong> | |
</slot> | |
<transition name="fade"> | |
<div v-show="open" class="popover-content" :class="style" @mouseover="onEnter"> | |
<slot name="content"> | |
<strong>Content slot required</strong> | |
</slot> | |
<div class="popover-arrow"></div> | |
</div> | |
</transition> | |
</div> | |
</template> | |
<script> | |
import { debounce } from 'lodash'; | |
const POPOVER_TIMEOUT = 300; | |
export default { | |
name: 'Popover', | |
props: { | |
type: { | |
type: String, | |
required: false, | |
default: '', | |
}, | |
}, | |
data() { | |
return { | |
open: false, | |
}; | |
}, | |
computed: { | |
style() { | |
return ['negative', 'danger', 'warning'].indexOf(this.type) > -1 ? 'popover-content--negative' : ''; | |
}, | |
}, | |
mounted() { | |
document.body.addEventListener('click', this.handleOutsideAction.bind(this)); | |
}, | |
destroyed() { | |
document.body.removeEventListener('click', this.handleOutsideAction.bind(this)); | |
}, | |
methods: { | |
handleOutsideAction({ target }) { | |
// close the popover if you click outside it | |
if (!this.$refs.popover.contains(target)) { | |
this.open = false; | |
} | |
}, | |
onEnter() { | |
this.open = true; | |
}, | |
onLeave: debounce(function() { | |
this.open = false; | |
}, POPOVER_TIMEOUT), | |
}, | |
}; | |
</script> | |
<style lang="scss"> | |
$popover-arrow-size: 0.5rem; | |
$color-mono-400: #333; | |
$color-negative: #fd6666; | |
.popover { | |
position: relative; | |
&-content { | |
@apply .absolute .left-0 .bottom-0 .border .border-mono-400 .bg-white .z-20 .p-4 .shadow-sm; | |
min-width: 200px; | |
// remove the width and the border size | |
transform: translate(calc(-70% + #{$popover-arrow-size} + 2px), calc(#{$popover-arrow-size} * 4 * -1)); | |
@screen md { | |
transform: translate(calc(-50% + #{$popover-arrow-size} + 2px), calc(#{$popover-arrow-size} * 4 * -1)); | |
} | |
.popover-arrow { | |
@apply .absolute .z-10 .w-2 .h-2; | |
border-top: $popover-arrow-size solid $color-mono-400; | |
border-right: $popover-arrow-size solid transparent; | |
border-bottom: $popover-arrow-size solid transparent; | |
border-left: $popover-arrow-size solid transparent; | |
bottom: calc(#{$popover-arrow-size} * 2 * -1); // same as the border width * 2 | |
// add border width to left position | |
left: calc(70% - #{$popover-arrow-size}); | |
@screen md { | |
// add border width to left position | |
left: calc(50% - #{$popover-arrow-size}); | |
} | |
} | |
&--warning, | |
&--danger, | |
&--negative { | |
@apply .border-negative; | |
.popover-arrow { | |
border-top: $popover-arrow-size solid $color-negative; | |
border-right: $popover-arrow-size solid transparent; | |
border-bottom: $popover-arrow-size solid transparent; | |
border-left: $popover-arrow-size solid transparent; | |
} | |
} | |
} | |
} | |
</style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment