uiv
Version:
Bootstrap 3 components implemented by Vue 2.
272 lines (260 loc) • 8.06 kB
JavaScript
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill
function isBoolean (obj) {
return typeof obj === 'boolean'
}
var EVENTS = {
MOUSE_ENTER: 'mouseenter',
MOUSE_LEAVE: 'mouseleave',
MOUSE_DOWN: 'mousedown',
MOUSE_UP: 'mouseup',
FOCUS: 'focus',
BLUR: 'blur',
CLICK: 'click',
INPUT: 'input',
KEY_DOWN: 'keydown',
KEY_UP: 'keyup',
KEY_PRESS: 'keypress',
RESIZE: 'resize',
SCROLL: 'scroll',
TOUCH_START: 'touchstart',
TOUCH_END: 'touchend'
};
function on (element, event, handler) {
/* istanbul ignore next */
element.addEventListener(event, handler);
}
function off (element, event, handler) {
/* istanbul ignore next */
element.removeEventListener(event, handler);
}
function isElement (el) {
return el && el.nodeType === Node.ELEMENT_NODE
}
function setDropdownPosition (dropdown, trigger, options) {
if ( options === void 0 ) options = {};
var doc = document.documentElement;
var containerScrollLeft = (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0);
var containerScrollTop = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);
var rect = trigger.getBoundingClientRect();
var dropdownRect = dropdown.getBoundingClientRect();
dropdown.style.right = 'auto';
dropdown.style.bottom = 'auto';
if (options.menuRight) {
dropdown.style.left = containerScrollLeft + rect.left + rect.width - dropdownRect.width + 'px';
} else {
dropdown.style.left = containerScrollLeft + rect.left + 'px';
}
if (options.dropup) {
dropdown.style.top = containerScrollTop + rect.top - dropdownRect.height - 4 + 'px';
} else {
dropdown.style.top = containerScrollTop + rect.top + rect.height + 'px';
}
}
function focus (el) {
if (!isElement(el)) {
return
}
el.getAttribute('tabindex') ? null : el.setAttribute('tabindex', '-1');
el.focus();
}
var DEFAULT_TAG = 'div';
var Dropdown = {
render: function render (h) {
return h(
this.tag,
{
class: {
'btn-group': this.tag === DEFAULT_TAG,
dropdown: !this.dropup,
dropup: this.dropup,
open: this.show
}
},
[
this.$slots.default,
h(
'ul',
{
class: {
'dropdown-menu': true,
'dropdown-menu-right': this.menuRight
},
ref: 'dropdown'
},
[this.$slots.dropdown]
)
]
)
},
props: {
tag: {
type: String,
default: DEFAULT_TAG
},
appendToBody: {
type: Boolean,
default: false
},
value: Boolean,
dropup: {
type: Boolean,
default: false
},
menuRight: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
notCloseElements: Array,
positionElement: null
},
data: function data () {
return {
show: false,
triggerEl: undefined
}
},
watch: {
value: function value (v) {
this.toggle(v);
}
},
mounted: function mounted () {
this.initTrigger();
if (this.triggerEl) {
on(this.triggerEl, EVENTS.CLICK, this.toggle);
on(this.triggerEl, EVENTS.KEY_DOWN, this.onKeyPress);
}
on(this.$refs.dropdown, EVENTS.KEY_DOWN, this.onKeyPress);
on(window, EVENTS.CLICK, this.windowClicked);
on(window, EVENTS.TOUCH_END, this.windowClicked);
if (this.value) {
this.toggle(true);
}
},
beforeDestroy: function beforeDestroy () {
this.removeDropdownFromBody();
if (this.triggerEl) {
off(this.triggerEl, EVENTS.CLICK, this.toggle);
off(this.triggerEl, EVENTS.KEY_DOWN, this.onKeyPress);
}
off(this.$refs.dropdown, EVENTS.KEY_DOWN, this.onKeyPress);
off(window, EVENTS.CLICK, this.windowClicked);
off(window, EVENTS.TOUCH_END, this.windowClicked);
},
methods: {
getFocusItem: function getFocusItem () {
var dropdownEl = this.$refs.dropdown;
/* istanbul ignore next */
return dropdownEl.querySelector('li > a:focus')
},
onKeyPress: function onKeyPress (event) {
if (this.show) {
var dropdownEl = this.$refs.dropdown;
var keyCode = event.keyCode;
if (keyCode === 27) {
// esc
this.toggle(false);
this.triggerEl && this.triggerEl.focus();
} else if (keyCode === 13) {
// enter
var currentFocus = this.getFocusItem();
currentFocus && currentFocus.click();
} else if (keyCode === 38 || keyCode === 40) {
// up || down
event.preventDefault();
event.stopPropagation();
var currentFocus$1 = this.getFocusItem();
var items = dropdownEl.querySelectorAll('li:not(.disabled) > a');
if (!currentFocus$1) {
focus(items[0]);
} else {
for (var i = 0; i < items.length; i++) {
if (currentFocus$1 === items[i]) {
if (keyCode === 38 && i < items.length > 0) {
focus(items[i - 1]);
} else if (keyCode === 40 && i < items.length - 1) {
focus(items[i + 1]);
}
break
}
}
}
}
}
},
initTrigger: function initTrigger () {
var trigger = this.$el.querySelector('[data-role="trigger"]') || this.$el.querySelector('.dropdown-toggle') || this.$el.firstChild;
this.triggerEl = trigger && trigger !== this.$refs.dropdown ? trigger : null;
},
toggle: function toggle (show) {
if (this.disabled) {
return
}
if (isBoolean(show)) {
this.show = show;
} else {
this.show = !this.show;
}
if (this.appendToBody) {
this.show ? this.appendDropdownToBody() : this.removeDropdownFromBody();
}
this.$emit('input', this.show);
},
windowClicked: function windowClicked (event) {
var target = event.target;
if (this.show && target) {
var targetInNotCloseElements = false;
if (this.notCloseElements) {
for (var i = 0, l = this.notCloseElements.length; i < l; i++) {
var isTargetInElement = this.notCloseElements[i].contains(target);
var shouldBreak = isTargetInElement;
/* istanbul ignore else */
if (this.appendToBody) {
var isTargetInDropdown = this.$refs.dropdown.contains(target);
var isElInElements = this.notCloseElements.indexOf(this.$el) >= 0;
shouldBreak = isTargetInElement || (isTargetInDropdown && isElInElements);
}
if (shouldBreak) {
targetInNotCloseElements = true;
break
}
}
}
var targetInDropdownBody = this.$refs.dropdown.contains(target);
var targetInTrigger = this.$el.contains(target) && !targetInDropdownBody;
// normally, a dropdown select event is handled by @click that trigger after @touchend
// then @touchend event have to be ignore in this case
var targetInDropdownAndIsTouchEvent = targetInDropdownBody && event.type === 'touchend';
if (!targetInTrigger && !targetInNotCloseElements && !targetInDropdownAndIsTouchEvent) {
this.toggle(false);
}
}
},
appendDropdownToBody: function appendDropdownToBody () {
try {
var el = this.$refs.dropdown;
el.style.display = 'block';
document.body.appendChild(el);
var positionElement = this.positionElement || this.$el;
setDropdownPosition(el, positionElement, this);
} catch (e) {
// Silent
}
},
removeDropdownFromBody: function removeDropdownFromBody () {
try {
var el = this.$refs.dropdown;
el.removeAttribute('style');
this.$el.appendChild(el);
} catch (e) {
// Silent
}
}
}
};
export default Dropdown;
//# sourceMappingURL=Dropdown.js.map