Tooltip component now works on mobile

This commit is contained in:
Kacper Donat 2020-01-28 21:57:58 +01:00
parent f9c289aacd
commit 519dc8de22
7 changed files with 108 additions and 25 deletions

View File

@ -46,6 +46,7 @@
"mini-css-extract-plugin": "^0.4.2",
"portal-vue": "^2.1.7",
"vue-dragscroll": "^1.10.2",
"vue-fragment": "^1.5.1",
"vue-removed-hook-mixin": "^0.1.1",
"vue2-leaflet": "^1.0.2",
"vuex": "^3.0.1",

View File

@ -1,7 +1,10 @@
<portal to="popups">
<transition name="fade">
<popper class="popper--tooltip" arrow :reference="element" :placement="placement" v-if="show" :responsive="false">
<slot />
</popper>
</transition>
</portal>
<fragment>
<portal to="popups">
<transition name="tooltip">
<popper class="popper--tooltip" aria-hidden="true" arrow :reference="root" :placement="placement" v-if="show" :responsive="false">
<slot />
</popper>
</transition>
</portal>
<span ref="root" class="sr-only"><slot /></span>
</fragment>

View File

@ -14,7 +14,16 @@
}
}
@include vue-animation(fade, 150ms ease-in-out) {
@include vue-animation(fade, 250ms ease-in-out) {
0% {
opacity: 0
}
100% {
opacity: 1
}
}
@include vue-animation(tooltip, 100ms ease-in-out) {
0% {
opacity: 0
}

View File

@ -15,16 +15,27 @@ import Vue from "vue";
import Vuex from 'vuex';
import PortalVue from 'portal-vue';
import VueDragscroll from 'vue-dragscroll';
import { Plugin as VueFragment } from 'vue-fragment';
import { Workbox } from "workbox-window";
import { migrate } from "./store/migrations";
import { Component } from "vue-property-decorator";
Vue.use(Vuex);
Vue.use(PortalVue);
Vue.use(VueDragscroll);
Vue.use(VueFragment);
declare module 'vue/types/vue' {
interface Vue {
$isTouch: boolean;
}
}
Vue.prototype.$isTouch = 'ontouchstart' in window || navigator.msMaxTouchPoints > 0;
Component.registerHooks(['removed']);
// async dependencies
(async function () {
await migrate("vuex");

View File

@ -1,34 +1,88 @@
import Vue from 'vue';
import Component from "vue-class-component";
import { Prop } from "vue-property-decorator";
import { Prop, Watch } from "vue-property-decorator";
type Events = {
[evnet: string]: (...any) => void,
}
type Trigger = "hover" | "focus";
const longPressTimeout = 1000;
@Component({ template: require('../../components/tooltip.html') })
export class TooltipComponent extends Vue {
@Prop({ type: String, default: "auto" }) public placement: string;
@Prop({ type: Number, default: 100 }) public delay: number;
@Prop({ type: Number, default: 200 }) public delay: number;
@Prop({ type: Array, default: ["hover", "focus"]}) public triggers: Trigger[];
public show: boolean = false;
public element: Element = null;
public root: Element = null;
private _events: { [event: string]: any };
private _timeout: number;
private _events: Events;
mounted() {
this.$el.parentElement.addEventListener('mouseenter', this._events['mouseenter'] = () => {
this._timeout = window.setTimeout(() => { this.show = true }, this.delay);
});
this.root = (this.$refs['root'] as HTMLSpanElement).parentElement;
this.$el.parentElement.addEventListener('mouseleave', this._events['mouseleave'] = () => {
window.clearTimeout(this._timeout);
this.show = false
});
this.element = this.$el.parentElement;
this._registerEventListeners();
}
beforeDestroy() {
this.$el.parentElement.removeEventListener('mouseenter', this._events['mouseenter']);
this.$el.parentElement.removeEventListener('mouseleave', this._events['mouseleave']);
this._removeEventListeners();
}
@Watch('triggers', { immediate: true })
updateTriggers() {
this._removeEventListeners();
this._events = {};
let blocked: boolean = false;
if (this.triggers.includes("hover")) {
let timeout;
this._events['mouseenter'] = () => {
timeout = window.setTimeout(() => { this.show = !blocked }, this.delay);
};
this._events['mouseleave'] = () => {
window.clearTimeout(timeout);
this.show = false
};
}
if (this.triggers.includes("focus") || (this.triggers.includes("hover") && this.$isTouch)) {
if (this.$isTouch) {
this._events['touchstart'] = () => {
// this is to prevent showing tooltips after tap
blocked = true;
setTimeout(() => blocked = false, longPressTimeout);
}
}
this._events['focus'] = () => {
this.show = !blocked;
};
this._events['blur'] = () => {
this.show = false
};
}
this._registerEventListeners();
}
private _registerEventListeners() {
for (const [event, handler] of Object.entries(this._events)) {
this.root.addEventListener(event, handler);
}
}
private _removeEventListeners() {
for (const [event, handler] of Object.entries(this._events)) {
this.root.removeEventListener(event, handler);
}
}
}

View File

@ -104,7 +104,7 @@ export class PopperComponent extends Vue {
}
removed() {
this._popper.destroy();
this._popper.destroy()
}
}

View File

@ -6453,6 +6453,11 @@ vue-dragscroll@^1.10.2:
resolved "https://registry.yarnpkg.com/vue-dragscroll/-/vue-dragscroll-1.10.2.tgz#34ace3c6aa7a39edc157cac5e9ea1d5b31b1119c"
integrity sha512-fGVw8KP3ggbp49csa1Tbj2my0YuNmZ1zxYYge4QWIypGNHVwd9hResy/v6QF5HxY0a+qd2EBteeBpxtJxFMp5A==
vue-fragment@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/vue-fragment/-/vue-fragment-1.5.1.tgz#44c070d55ed1e9a6c698ef57a5c83f64bb06feeb"
integrity sha512-ig6eES6TcMBbANW71ylB+AJgRN+Zksb3f50AxjGpAk6hMzqmeuD80qeh4LJP0jVw2dMBMjgRUfIkrvxygoRgtQ==
vue-property-decorator@^7.0.0:
version "7.3.0"
resolved "https://registry.yarnpkg.com/vue-property-decorator/-/vue-property-decorator-7.3.0.tgz#d50d67f0b0f1c814f9f2fba36d6eeccbcc62dbb6"