216 lines
5.6 KiB
JavaScript
216 lines
5.6 KiB
JavaScript
import {closest, css, getEventPos, includes, isRtl, isTouch, off, on, pointerCancel, pointerDown, pointerMove, pointerUp, selInput, trigger} from 'uikit-util';
|
|
|
|
export default {
|
|
|
|
props: {
|
|
draggable: Boolean
|
|
},
|
|
|
|
data: {
|
|
draggable: true,
|
|
threshold: 10
|
|
},
|
|
|
|
created() {
|
|
|
|
['start', 'move', 'end'].forEach(key => {
|
|
|
|
const fn = this[key];
|
|
this[key] = e => {
|
|
|
|
const pos = getEventPos(e).x * (isRtl ? -1 : 1);
|
|
|
|
this.prevPos = pos !== this.pos ? this.pos : this.prevPos;
|
|
this.pos = pos;
|
|
|
|
fn(e);
|
|
};
|
|
|
|
});
|
|
|
|
},
|
|
|
|
events: [
|
|
|
|
{
|
|
|
|
name: pointerDown,
|
|
|
|
delegate() {
|
|
return this.selSlides;
|
|
},
|
|
|
|
handler(e) {
|
|
|
|
if (!this.draggable
|
|
|| !isTouch(e) && hasTextNodesOnly(e.target)
|
|
|| closest(e.target, selInput)
|
|
|| e.button > 0
|
|
|| this.length < 2
|
|
) {
|
|
return;
|
|
}
|
|
|
|
this.start(e);
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
name: 'dragstart',
|
|
|
|
handler(e) {
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
|
|
],
|
|
|
|
methods: {
|
|
|
|
start() {
|
|
|
|
this.drag = this.pos;
|
|
|
|
if (this._transitioner) {
|
|
|
|
this.percent = this._transitioner.percent();
|
|
this.drag += this._transitioner.getDistance() * this.percent * this.dir;
|
|
|
|
this._transitioner.cancel();
|
|
this._transitioner.translate(this.percent);
|
|
|
|
this.dragging = true;
|
|
|
|
this.stack = [];
|
|
|
|
} else {
|
|
this.prevIndex = this.index;
|
|
}
|
|
|
|
on(document, pointerMove, this.move, {passive: false});
|
|
|
|
// 'input' event is triggered by video controls
|
|
on(document, `${pointerUp} ${pointerCancel} input`, this.end, true);
|
|
|
|
css(this.list, 'userSelect', 'none');
|
|
|
|
},
|
|
|
|
move(e) {
|
|
|
|
const distance = this.pos - this.drag;
|
|
|
|
if (distance === 0 || this.prevPos === this.pos || !this.dragging && Math.abs(distance) < this.threshold) {
|
|
return;
|
|
}
|
|
|
|
// prevent click event
|
|
css(this.list, 'pointerEvents', 'none');
|
|
|
|
e.cancelable && e.preventDefault();
|
|
|
|
this.dragging = true;
|
|
this.dir = (distance < 0 ? 1 : -1);
|
|
|
|
const {slides} = this;
|
|
let {prevIndex} = this;
|
|
let dis = Math.abs(distance);
|
|
let nextIndex = this.getIndex(prevIndex + this.dir, prevIndex);
|
|
let width = this._getDistance(prevIndex, nextIndex) || slides[prevIndex].offsetWidth;
|
|
|
|
while (nextIndex !== prevIndex && dis > width) {
|
|
|
|
this.drag -= width * this.dir;
|
|
|
|
prevIndex = nextIndex;
|
|
dis -= width;
|
|
nextIndex = this.getIndex(prevIndex + this.dir, prevIndex);
|
|
width = this._getDistance(prevIndex, nextIndex) || slides[prevIndex].offsetWidth;
|
|
|
|
}
|
|
|
|
this.percent = dis / width;
|
|
|
|
const prev = slides[prevIndex];
|
|
const next = slides[nextIndex];
|
|
const changed = this.index !== nextIndex;
|
|
const edge = prevIndex === nextIndex;
|
|
|
|
let itemShown;
|
|
|
|
[this.index, this.prevIndex].filter(i => !includes([nextIndex, prevIndex], i)).forEach(i => {
|
|
trigger(slides[i], 'itemhidden', [this]);
|
|
|
|
if (edge) {
|
|
itemShown = true;
|
|
this.prevIndex = prevIndex;
|
|
}
|
|
|
|
});
|
|
|
|
if (this.index === prevIndex && this.prevIndex !== prevIndex || itemShown) {
|
|
trigger(slides[this.index], 'itemshown', [this]);
|
|
}
|
|
|
|
if (changed) {
|
|
this.prevIndex = prevIndex;
|
|
this.index = nextIndex;
|
|
|
|
!edge && trigger(prev, 'beforeitemhide', [this]);
|
|
trigger(next, 'beforeitemshow', [this]);
|
|
}
|
|
|
|
this._transitioner = this._translate(Math.abs(this.percent), prev, !edge && next);
|
|
|
|
if (changed) {
|
|
!edge && trigger(prev, 'itemhide', [this]);
|
|
trigger(next, 'itemshow', [this]);
|
|
}
|
|
|
|
},
|
|
|
|
end() {
|
|
|
|
off(document, pointerMove, this.move, {passive: false});
|
|
off(document, `${pointerUp} ${pointerCancel} input`, this.end, true);
|
|
|
|
if (this.dragging) {
|
|
|
|
this.dragging = null;
|
|
|
|
if (this.index === this.prevIndex) {
|
|
this.percent = 1 - this.percent;
|
|
this.dir *= -1;
|
|
this._show(false, this.index, true);
|
|
this._transitioner = null;
|
|
} else {
|
|
|
|
const dirChange = (isRtl ? this.dir * (isRtl ? 1 : -1) : this.dir) < 0 === this.prevPos > this.pos;
|
|
this.index = dirChange ? this.index : this.prevIndex;
|
|
|
|
if (dirChange) {
|
|
this.percent = 1 - this.percent;
|
|
}
|
|
|
|
this.show(this.dir > 0 && !dirChange || this.dir < 0 && dirChange ? 'next' : 'previous', true);
|
|
}
|
|
|
|
}
|
|
|
|
css(this.list, {userSelect: '', pointerEvents: ''});
|
|
|
|
this.drag
|
|
= this.percent
|
|
= null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
function hasTextNodesOnly(el) {
|
|
return !el.children.length && el.childNodes.length;
|
|
}
|