140 lines
3.7 KiB
TypeScript
140 lines
3.7 KiB
TypeScript
import Vue from 'vue';
|
|
import { Component, Prop, Watch } from "vue-property-decorator";
|
|
import Popper, { Placement } from "popper.js";
|
|
import { Portal } from "portal-vue";
|
|
|
|
@Component({
|
|
template: require("../../components/popper.html")
|
|
})
|
|
export class PopperComponent extends Vue {
|
|
@Prop(String)
|
|
public reference: string;
|
|
|
|
@Prop(Object)
|
|
public refs: string;
|
|
|
|
@Prop({ type: String, default: "auto" })
|
|
public placement: Placement;
|
|
|
|
@Prop(Boolean)
|
|
public arrow: boolean;
|
|
|
|
private _event;
|
|
private _popper;
|
|
|
|
focusOut(event: MouseEvent) {
|
|
if (this.$el.contains(event.target as Node)) {
|
|
return;
|
|
}
|
|
|
|
this.$emit('leave', event);
|
|
}
|
|
|
|
mounted() {
|
|
const reference = this.refsSource[this.reference] as HTMLElement;
|
|
|
|
this._popper = new Popper(reference, this.$el, {
|
|
placement: this.placement,
|
|
modifiers: {
|
|
arrow: { enabled: this.arrow, element: this.$refs['arrow'] as Element },
|
|
responsive: {
|
|
enabled: true,
|
|
order: 890,
|
|
fn(data) {
|
|
if (window.innerWidth < 560) {
|
|
data.instance.options.placement = 'bottom';
|
|
data.styles.transform = `translate3d(0, ${data.offsets.popper.top}px, 0)`;
|
|
data.styles.right = '0';
|
|
data.styles.left = '0';
|
|
data.styles.width = 'auto';
|
|
data.arrowStyles.left = `${data.offsets.popper.left + data.offsets.arrow.left}px`;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
this.$nextTick(() => {
|
|
this._popper.update();
|
|
document.addEventListener('click', this._event = this.focusOut.bind(this), { capture: true });
|
|
});
|
|
}
|
|
|
|
updated() {
|
|
this._popper.update();
|
|
}
|
|
|
|
@Watch('visible')
|
|
private onVisibilityUpdate() {
|
|
this._popper.update();
|
|
window.dispatchEvent(new Event('resize'));
|
|
}
|
|
|
|
beforeDestroy() {
|
|
this._popper.destroy();
|
|
this._event && document.removeEventListener('click', this._event, { capture: true });
|
|
}
|
|
|
|
get refsSource() {
|
|
if (this.refs) {
|
|
return this.refs;
|
|
}
|
|
|
|
if (this.$parent.$options.name == 'portalTarget') {
|
|
return this.$parent.$parent.$refs;
|
|
}
|
|
|
|
return this.$parent.$refs
|
|
}
|
|
}
|
|
|
|
@Component({ template: require('../../components/fold.html') })
|
|
export class FoldComponent extends Vue {
|
|
private observer: MutationObserver;
|
|
|
|
@Prop(Boolean)
|
|
public visible: boolean;
|
|
|
|
@Prop(Boolean)
|
|
public lazy: boolean;
|
|
|
|
mounted() {
|
|
this.resize();
|
|
|
|
this.observer = new MutationObserver(() => this.resize());
|
|
this.observer.observe(this.$refs['inner'] as Node, {
|
|
characterData: true,
|
|
subtree: true,
|
|
childList: true
|
|
});
|
|
}
|
|
|
|
beforeDestroy() {
|
|
this.observer.disconnect();
|
|
}
|
|
|
|
@Watch('visible')
|
|
private resize() {
|
|
const inner = this.$refs['inner'] as HTMLDivElement;
|
|
(this.$el as HTMLElement).style.height = `${(this.visible && inner) ? inner.clientHeight : 0}px`;
|
|
}
|
|
}
|
|
|
|
@Component({ template: require("../../components/lazy.html") })
|
|
export class LazyComponent extends Vue {
|
|
@Prop(Boolean)
|
|
public activate: boolean;
|
|
protected visible: boolean = false;
|
|
|
|
@Watch('activate')
|
|
private onVisibilityChange(value, old) {
|
|
this.visible = value || old;
|
|
}
|
|
}
|
|
|
|
Vue.component('Popper', PopperComponent);
|
|
Vue.component('Fold', FoldComponent);
|
|
Vue.component('Lazy', LazyComponent);
|