Add tooltip component

This commit is contained in:
Kacper Donat 2020-01-26 22:29:16 +01:00
parent 4bd5ee14ca
commit ce8c8f97ec
7 changed files with 87 additions and 18 deletions

View File

@ -6,7 +6,8 @@
</div> </div>
<div class="departure__time"> <div class="departure__time">
<fa-layers v-if="!departure.estimated" class="mr-1" title="Czas rozkładowy, nieuwzględniający aktualnej sytuacji komunikacyjnej."> <fa-layers v-if="!departure.estimated" class="mr-1">
<tooltip placement="top-end">Czas rozkładowy, nieuwzględniający aktualnej sytuacji komunikacyjnej.</tooltip>
<fa :icon="['far', 'clock']"/> <fa :icon="['far', 'clock']"/>
<fa :icon="['fas', 'exclamation-triangle']" transform="shrink-5 down-4 right-6"/> <fa :icon="['fas', 'exclamation-triangle']" transform="shrink-5 down-4 right-6"/>
</fa-layers> </fa-layers>

View File

@ -1,4 +1,4 @@
<div class="popper" :class="{ 'popper--arrow': arrow }" v-on="$listeners"> <div :class="[ 'popper', arrow && 'popper--arrow' ]" v-on="$listeners">
<div class="popper__arrow" ref="arrow" v-if="arrow"></div> <div class="popper__arrow" ref="arrow" v-if="arrow"></div>
<slot /> <slot />
</div> </div>

View File

@ -0,0 +1,5 @@
<portal to="popups">
<popper class="popper--tooltip" arrow :reference="element" :placement="placement" v-if="show">
<slot />
</popper>
</portal>

View File

@ -108,12 +108,38 @@
} }
} }
&.popper--arrow { @mixin arrows {
@include placement("left"); @include placement("left");
@include placement("right"); @include placement("right");
@include placement("top"); @include placement("top");
@include placement("bottom"); @include placement("bottom");
} }
&.popper--arrow {
@include arrows;
}
&.popper--tooltip {
background: $dark;
color: white;
padding: .5rem .75rem;
font-size: $small-font-size;
font-weight: bold;
min-width: 0;
box-shadow: none;
&.popper--arrow {
$arrow-color: $dark;
$arrow-border: none;
$arrow-base: 6px;
@include arrows;
.popper__arrow::before {
border: none;
}
}
}
} }
@include media-breakpoint-down('sm') { @include media-breakpoint-down('sm') {

View File

@ -1,3 +1,4 @@
export * from './tooltip';
export * from './utils' export * from './utils'
export * from './line' export * from './line'
export * from './picker' export * from './picker'

View File

@ -0,0 +1,26 @@
import Vue from 'vue';
import Component from "vue-class-component";
import { Prop } from "vue-property-decorator";
@Component({ template: require('../../components/tooltip.html') })
export class TooltipComponent extends Vue {
@Prop({ type: String, default: "auto" }) public placement: string;
public show: boolean = false;
public element: Element = null;
private _events: { [event: string]: any };
mounted() {
this.$el.parentElement.addEventListener('mouseenter', this._events['mouseenter'] = () => this.show = true);
this.$el.parentElement.addEventListener('mouseleave', this._events['mouseleave'] = () => this.show = false);
this.element = this.$el.parentElement;
}
beforeDestroy() {
this.$el.parentElement.removeEventListener('mouseenter', this._events['mouseenter']);
this.$el.parentElement.removeEventListener('mouseleave', this._events['mouseleave']);
}
}
Vue.component('Tooltip', TooltipComponent);

View File

@ -7,8 +7,8 @@ import { Portal } from "portal-vue";
template: require("../../components/popper.html") template: require("../../components/popper.html")
}) })
export class PopperComponent extends Vue { export class PopperComponent extends Vue {
@Prop(String) @Prop([ String, HTMLElement ])
public reference: string; public reference: string | HTMLElement;
@Prop(Object) @Prop(Object)
public refs: string; public refs: string;
@ -22,6 +22,28 @@ export class PopperComponent extends Vue {
private _event; private _event;
private _popper; private _popper;
private getReferenceElement() {
const isInPortal = this.$parent.$options.name == 'portalTarget';
if (typeof this.reference === 'string') {
if (this.refs) {
return this.refs[this.reference];
}
if (isInPortal) {
return this.$parent.$parent.$refs[this.reference];
}
return this.$parent.$refs[this.reference];
}
if (this.reference instanceof HTMLElement) {
return this.reference;
}
return isInPortal ? this.$parent.$el : this.$el.parentElement;
}
focusOut(event: MouseEvent) { focusOut(event: MouseEvent) {
if (this.$el.contains(event.target as Node)) { if (this.$el.contains(event.target as Node)) {
return; return;
@ -31,7 +53,7 @@ export class PopperComponent extends Vue {
} }
mounted() { mounted() {
const reference = this.refsSource[this.reference] as HTMLElement; const reference = this.getReferenceElement();
this._popper = new Popper(reference, this.$el, { this._popper = new Popper(reference, this.$el, {
placement: this.placement, placement: this.placement,
@ -76,18 +98,6 @@ export class PopperComponent extends Vue {
this._popper.destroy(); this._popper.destroy();
this._event && document.removeEventListener('click', this._event, { capture: true }); 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') }) @Component({ template: require('../../components/fold.html') })