import * as easingPatterns from './easing-patterns' import { getContainer, getOffset } from './util' import Vue from 'vue' type GoToTarget = number | string | HTMLElement | Vue interface GoToSettings { container: string | HTMLElement | Vue duration: number offset: number easing: string | easingPatterns.EasingFunction appOffset: boolean } export default function goTo (_target: GoToTarget, _settings: Partial = {}): Promise { const settings: GoToSettings = { container: (document.scrollingElement as HTMLElement | null) || document.body || document.documentElement, duration: 500, offset: 0, easing: 'easeInOutCubic', appOffset: true, ..._settings } const container = getContainer(settings.container) if (settings.appOffset) { const isDrawer = container.classList.contains('v-navigation-drawer') const isClipped = container.classList.contains('v-navigation-drawer--clipped') settings.offset += Vue.prototype.$vuetify.application.bar if (!isDrawer || isClipped) settings.offset += Vue.prototype.$vuetify.application.top } const startTime = performance.now() const targetLocation = getOffset(_target) - settings.offset const startLocation = container.scrollTop if (targetLocation === startLocation) return Promise.resolve(targetLocation) const ease = typeof settings.easing === 'function' ? settings.easing : (easingPatterns as Dictionary)[settings.easing] if (!ease) throw new TypeError(`Easing function "${settings.easing}" not found.`) // tslint:disable-next-line:promise-must-complete return new Promise(resolve => requestAnimationFrame(function step (currentTime: number) { const timeElapsed = currentTime - startTime const progress = Math.abs(settings.duration ? Math.min(timeElapsed / settings.duration, 1) : 1) container.scrollTop = Math.floor(startLocation + (targetLocation - startLocation) * ease(progress)) if (progress === 1 || container.clientHeight + container.scrollTop === container.scrollHeight) { return resolve(targetLocation) } requestAnimationFrame(step) })) }