UNPKG

uikit

Version:

UIkit is a lightweight and modular front-end framework for developing fast and powerful web interfaces.

215 lines (169 loc) 6.05 kB
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() { for (const key of ['start', 'move', 'end']) { const fn = this[key]; this[key] = (e) => { const pos = getEventPos(e).x * (isRtl ? -1 : 1); this.prevPos = pos === this.pos ? this.prevPos : this.pos; 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; }