nuxt-anchorscroll
Version:
Scroll with anchor support for Nuxt
95 lines (94 loc) • 3.1 kB
JavaScript
import { toValue } from "vue";
import { defineNuxtPlugin, useNuxtApp, useRuntimeConfig } from "nuxt/app";
const generalAnchorScroll = ({ hash }) => {
const scrollVariants = {
toTop: {
scrollOptions: toValue(useNuxtApp().$anchorScroll?.defaults.toTop) || {}
}
};
if (hash !== "") {
try {
const target = document.querySelector(hash);
if (!target) {
console.error(`[AnchorScroll]: unable to find element with selector '${hash}'`);
return scrollVariants;
}
scrollVariants.toAnchor = {
target,
scrollOptions: toValue(useNuxtApp().$anchorScroll?.defaults.toAnchor) ?? {}
};
} catch (error) {
console.error(`[AnchorScroll]: unable to get element for selector '${hash}':`, error);
}
}
return scrollVariants;
};
const anchorScrollExecutor = (hook) => {
const nuxtApp = useNuxtApp();
const currentRoute = nuxtApp.$router.currentRoute;
const disableAnchorScroll = currentRoute.value.meta.disableAnchorScroll ?? {};
const {
toAnchor: disableToAnchor = false,
toTop: disableToTop = false
} = disableAnchorScroll === true ? { toAnchor: true, toTop: true } : disableAnchorScroll;
if (disableToAnchor && disableToTop)
return;
const allMatched = [...nuxtApp?.$anchorScroll?.matched ?? [], generalAnchorScroll];
for (const matched of allMatched) {
const maybeAnchorScrollAlternatives = matched(currentRoute.value, hook);
if (maybeAnchorScrollAlternatives === false)
return;
const { toAnchor, toTop } = maybeAnchorScrollAlternatives ?? {};
if (!disableToAnchor && toAnchor) {
const {
target,
scrollOptions: { behavior, offsetLeft, offsetTop },
surfaces = toValue(nuxtApp.$anchorScroll?.defaults.surfaces) ?? []
} = toAnchor;
const { top, left } = target.getBoundingClientRect();
const scrollToAnchorOptions = {
behavior,
...offsetLeft !== void 0 && { left: left + offsetLeft },
...offsetTop !== void 0 && { top: top + offsetTop }
};
for (const surface of surfaces)
surface.scrollBy(scrollToAnchorOptions);
return;
}
if (!disableToTop && toTop) {
const {
scrollOptions: { behavior, offsetLeft, offsetTop },
surfaces = toValue(nuxtApp.$anchorScroll?.defaults.surfaces) ?? []
} = toTop;
const scrollToTopOptions = {
behavior,
left: offsetLeft,
top: offsetTop
};
for (const surface of surfaces)
surface.scrollTo(scrollToTopOptions);
}
}
};
export default defineNuxtPlugin((nuxtApp) => {
const {
hooks = []
} = useRuntimeConfig().public.anchorScroll ?? {};
useNuxtApp().$anchorScroll = {
matched: [],
general: generalAnchorScroll,
defaults: {
toAnchor: {
behavior: "smooth",
offsetTop: 0
},
toTop: {
behavior: "instant",
offsetTop: 0
},
surfaces: () => [document.documentElement, document.body]
}
};
for (const hook of hooks)
nuxtApp.hook(hook, () => anchorScrollExecutor(hook));
});