uikit
Version:
UIkit is a lightweight and modular front-end framework for developing fast and powerful web interfaces.
140 lines (103 loc) • 3.56 kB
JavaScript
import Container from '../mixin/container';
import Togglable from '../mixin/togglable';
import Position from '../mixin/position';
import {append, attr, flipPosition, hasAttr, isInput, isTouch, matches, on, once, pointerDown, pointerEnter, pointerLeave, remove, within} from 'uikit-util';
export default {
mixins: [Container, Togglable, Position],
args: 'title',
props: {
delay: Number,
title: String
},
data: {
pos: 'top',
title: '',
delay: 0,
animation: ['uk-animation-scale-up'],
duration: 100,
cls: 'uk-active',
clsPos: 'uk-tooltip'
},
beforeConnect() {
this._hasTitle = hasAttr(this.$el, 'title');
attr(this.$el, 'title', '');
this.updateAria(false);
makeFocusable(this.$el);
},
disconnected() {
this.hide();
attr(this.$el, 'title', this._hasTitle ? this.title : null);
},
methods: {
show() {
if (this.isToggled(this.tooltip || null) || !this.title) {
return;
}
this._unbind = once(document, `show keydown ${pointerDown}`, this.hide, false, e =>
e.type === pointerDown && !within(e.target, this.$el)
|| e.type === 'keydown' && e.keyCode === 27
|| e.type === 'show' && e.detail[0] !== this && e.detail[0].$name === this.$name
);
clearTimeout(this.showTimer);
this.showTimer = setTimeout(this._show, this.delay);
},
hide() {
if (matches(this.$el, 'input:focus')) {
return;
}
clearTimeout(this.showTimer);
if (!this.isToggled(this.tooltip || null)) {
return;
}
this.toggleElement(this.tooltip, false, false).then(() => {
this.tooltip = remove(this.tooltip);
this._unbind();
});
},
_show() {
this.tooltip = append(this.container,
`<div class="${this.clsPos}">
<div class="${this.clsPos}-inner">${this.title}</div>
</div>`
);
on(this.tooltip, 'toggled', (e, toggled) => {
this.updateAria(toggled);
if (!toggled) {
return;
}
this.positionAt(this.tooltip, this.$el);
this.origin = this.getAxis() === 'y'
? `${flipPosition(this.dir)}-${this.align}`
: `${this.align}-${flipPosition(this.dir)}`;
});
this.toggleElement(this.tooltip, true);
},
updateAria(toggled) {
attr(this.$el, 'aria-expanded', toggled);
}
},
events: {
focus: 'show',
blur: 'hide',
[`${pointerEnter} ${pointerLeave}`](e) {
if (!isTouch(e)) {
this[e.type === pointerEnter ? 'show' : 'hide']();
}
},
// Clicking a button does not give it focus on all browsers and platforms
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#clicking_and_focus
[pointerDown](e) {
if (isTouch(e)) {
this.show();
}
}
}
};
function makeFocusable(el) {
if (!isFocusable(el)) {
attr(el, 'tabindex', '0');
}
}
function isFocusable(el) {
return isInput(el) || matches(el, 'a,button') || hasAttr(el, 'tabindex');
}