2
0
mirror of https://github.com/frappe/books.git synced 2024-12-23 03:19:01 +00:00

fix: Refactor Popover to use Portal and Popper.js

This commit is contained in:
Faris Ansari 2019-12-11 14:35:46 +05:30
parent df6ace3b8d
commit 466622f074
5 changed files with 78 additions and 26 deletions

View File

@ -63,9 +63,10 @@
"frappe-charts": "^1.2.4",
"frappejs": "https://github.com/frappe/frappejs",
"nodemailer": "^4.7.0",
"popper.js": "^1.14.4",
"vue-color": "^2.7.0",
"vue-toasted": "^1.1.25"
"vue-toasted": "^1.1.25",
"portal-vue": "2.1.6",
"popper.js": "1.16.0"
},
"devDependencies": {
"electron-builder": "^21.2.0"

View File

@ -4,6 +4,7 @@
<database-selector v-if="showDatabaseSelector" @file="connectToDBFile" />
<setup-wizard v-if="showSetupWizard" />
<Settings v-if="showSettings" />
<portal-target name="popovers" multiple></portal-target>
</div>
</template>

View File

@ -1,5 +1,5 @@
<template>
<Popover :right="true" @hide="emitFilterChange">
<Popover @close="emitFilterChange" right>
<template v-slot:target="{ toggleDropdown }">
<Button :icon="true" @click="toggleDropdown()">
<span class="flex items-center">
@ -19,7 +19,7 @@
<div class="p-3">
<template v-if="filters.length">
<div
:key="filter.fieldname + filter.conditions + filter.value"
:key="frappe.getRandomString()"
v-for="(filter, i) in filters"
class="flex items-center justify-between text-base"
:class="i !== 0 && 'mt-2'"

View File

@ -1,43 +1,91 @@
<template>
<div class="relative" v-on-outside-click="() => toggleDropdown(false)">
<div ref="reference">
<div class="h-full">
<slot name="target" :toggleDropdown="toggleDropdown"></slot>
</div>
<portal to="popovers">
<div
:class="right ? 'right-0' : 'left-0'"
class="mt-1 absolute z-10 bg-white rounded-5px border min-w-40 shadow-md"
v-if="isShown"
ref="popover"
:class="popoverClass"
class="mt-1 bg-white rounded-5px border min-w-40 shadow-md"
v-show="isOpen"
>
<slot name="content"></slot>
<slot name="content" :toggleDropdown="toggleDropdown"></slot>
</div>
</portal>
</div>
</template>
<script>
import Popper from 'popper.js';
export default {
name: 'Popover',
props: ['right'],
props: {
right: Boolean,
popoverClass: [String, Object, Array]
},
data() {
return {
isShown: false
isOpen: false
};
},
watch: {
isShown(newVal, oldVal) {
if (newVal === false) {
this.$emit('hide');
}
if (newVal === true) {
this.$emit('show');
}
mounted() {
let listener = e => {
let $els = [this.$refs.reference, this.$refs.popover];
let insideClick = $els.some(
$el => $el && (e.target === $el || $el.contains(e.target))
);
if (insideClick) {
return;
}
this.close();
};
document.addEventListener('click', listener);
this.$once('hook:beforeDestroy', () => {
document.removeEventListener('click', listener);
});
},
beforeDestroy() {
this.popper.destroy();
},
methods: {
toggleDropdown(flag, from) {
if (flag == null) {
flag = !this.isShown;
setupPopper() {
if (!this.popper) {
this.popper = new Popper(this.$refs.reference, this.$refs.popover, {
placement: this.right ? 'bottom-end' : 'bottom-start'
});
} else {
this.popper.scheduleUpdate();
}
this.isShown = Boolean(flag);
},
toggleDropdown(flag) {
if (flag == null) {
flag = !this.isOpen;
}
flag = Boolean(flag);
if (flag) {
this.open();
} else {
this.close();
}
},
open() {
if (this.isOpen) {
return;
}
this.isOpen = true;
this.$nextTick(() => {
this.setupPopper();
});
this.$emit('open');
},
close() {
if (!this.isOpen) {
return;
}
this.isOpen = false;
this.$emit('close');
}
}
};

View File

@ -11,6 +11,7 @@ import { ipcRenderer } from 'electron';
// vue imports
import Vue from 'vue';
import PortalVue from 'portal-vue';
import App from './App';
import router from './router';
@ -215,6 +216,7 @@ import router from './router';
Vue.config.productionTip = false;
Vue.component('feather-icon', FeatherIcon);
Vue.directive('on-outside-click', outsideClickDirective);
Vue.use(PortalVue);
Vue.mixin({
computed: {
frappe() {