105 lines
4.4 KiB
JavaScript
105 lines
4.4 KiB
JavaScript
// Utilities
|
|
import { inject } from 'vue';
|
|
import { mergeDeep, refElement } from "../util/index.mjs"; // Types
|
|
export const GoToSymbol = Symbol.for('vuetify:goto');
|
|
function genDefaults() {
|
|
return {
|
|
container: undefined,
|
|
duration: 300,
|
|
layout: false,
|
|
offset: 0,
|
|
easing: 'easeInOutCubic',
|
|
patterns: {
|
|
linear: t => t,
|
|
easeInQuad: t => t ** 2,
|
|
easeOutQuad: t => t * (2 - t),
|
|
easeInOutQuad: t => t < 0.5 ? 2 * t ** 2 : -1 + (4 - 2 * t) * t,
|
|
easeInCubic: t => t ** 3,
|
|
easeOutCubic: t => --t ** 3 + 1,
|
|
easeInOutCubic: t => t < 0.5 ? 4 * t ** 3 : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1,
|
|
easeInQuart: t => t ** 4,
|
|
easeOutQuart: t => 1 - --t ** 4,
|
|
easeInOutQuart: t => t < 0.5 ? 8 * t ** 4 : 1 - 8 * --t ** 4,
|
|
easeInQuint: t => t ** 5,
|
|
easeOutQuint: t => 1 + --t ** 5,
|
|
easeInOutQuint: t => t < 0.5 ? 16 * t ** 5 : 1 + 16 * --t ** 5
|
|
}
|
|
};
|
|
}
|
|
function getContainer(el) {
|
|
return getTarget(el) ?? (document.scrollingElement || document.body);
|
|
}
|
|
function getTarget(el) {
|
|
return typeof el === 'string' ? document.querySelector(el) : refElement(el);
|
|
}
|
|
function getOffset(target, horizontal, rtl) {
|
|
if (typeof target === 'number') return horizontal && rtl ? -target : target;
|
|
let el = getTarget(target);
|
|
let totalOffset = 0;
|
|
while (el) {
|
|
totalOffset += horizontal ? el.offsetLeft : el.offsetTop;
|
|
el = el.offsetParent;
|
|
}
|
|
return totalOffset;
|
|
}
|
|
export function createGoTo(options, locale) {
|
|
return {
|
|
rtl: locale.isRtl,
|
|
options: mergeDeep(genDefaults(), options)
|
|
};
|
|
}
|
|
async function scrollTo(_target, _options, horizontal, goTo) {
|
|
const options = mergeDeep(goTo?.options, _options);
|
|
const rtl = goTo?.rtl.value;
|
|
const target = (typeof _target === 'number' ? _target : getTarget(_target)) ?? 0;
|
|
const container = options.container === 'parent' && target instanceof HTMLElement ? target.parentElement : getContainer(options.container);
|
|
const ease = typeof options.easing === 'function' ? options.easing : options.patterns[options.easing];
|
|
if (!ease) throw new TypeError(`Easing function "${options.easing}" not found.`);
|
|
let targetLocation;
|
|
if (typeof target === 'number') {
|
|
targetLocation = getOffset(target, horizontal, rtl);
|
|
} else {
|
|
targetLocation = getOffset(target, horizontal, rtl) - getOffset(container, horizontal, rtl);
|
|
if (options.layout) {
|
|
const styles = window.getComputedStyle(target);
|
|
const layoutOffset = styles.getPropertyValue('--v-layout-top');
|
|
if (layoutOffset) targetLocation -= parseInt(layoutOffset, 10);
|
|
}
|
|
}
|
|
targetLocation += options.offset;
|
|
const startLocation = (horizontal ? container.scrollLeft : container.scrollTop) ?? 0;
|
|
if (targetLocation === startLocation) return Promise.resolve(targetLocation);
|
|
const startTime = performance.now();
|
|
return new Promise(resolve => requestAnimationFrame(function step(currentTime) {
|
|
const timeElapsed = currentTime - startTime;
|
|
const progress = Math.abs(options.duration ? Math.min(timeElapsed / options.duration, 1) : 1);
|
|
const location = Math.floor(startLocation + (targetLocation - startLocation) * ease(progress));
|
|
container[horizontal ? 'scrollLeft' : 'scrollTop'] = location;
|
|
if (progress === 1) return resolve(targetLocation);
|
|
let clientSize;
|
|
let reachEnd;
|
|
if (!horizontal) {
|
|
clientSize = container === document.body ? document.documentElement.clientHeight : container.clientHeight;
|
|
reachEnd = clientSize + container.scrollTop >= container.scrollHeight;
|
|
if (targetLocation > container.scrollTop && reachEnd) return resolve(targetLocation);
|
|
} else {
|
|
clientSize = container === document.body ? document.documentElement.clientWidth : container.clientWidth;
|
|
reachEnd = clientSize + container.scrollLeft >= container.scrollWidth;
|
|
if (targetLocation > container.scrollLeft && reachEnd) return resolve(targetLocation);
|
|
}
|
|
requestAnimationFrame(step);
|
|
}));
|
|
}
|
|
export function useGoTo() {
|
|
let _options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
const goTo = inject(GoToSymbol);
|
|
if (!goTo) throw new Error('[Vuetify] Could not find injected goto instance');
|
|
async function go(target, options) {
|
|
return scrollTo(target, mergeDeep(_options, options), false, goTo);
|
|
}
|
|
go.horizontal = async (target, options) => {
|
|
return scrollTo(target, mergeDeep(_options, options), true, goTo);
|
|
};
|
|
return go;
|
|
}
|
|
//# sourceMappingURL=goto.mjs.map
|