UNPKG

@shopware-ag/meteor-component-library

Version:

The meteor component library is a Vue component library developed by Shopware. It is based on the [Meteor Design System](https://shopware.design/).

151 lines (125 loc) 4.74 kB
import type { Ref } from "vue"; import { onUpdated, nextTick, onBeforeUnmount, onMounted, watch } from "vue"; import { throttle } from "@/utils/throttle"; /** * This composable expect a Vue reference to an HTML element. It sets * automatically the data attributes "data-scroll-(top|right|bottom|left)" * when the element is scrollable into this direction. */ export default function useScrollPossibilitiesClasses( refElement: Ref, showScrollIndicators: Ref<boolean>, ) { const setScrollPossibilitiesClasses = (elementOrEvent: Event | HTMLElement | undefined) => { // @ts-expect-error - check if event or not const element = (elementOrEvent?.target ? elementOrEvent.target : elementOrEvent) as | HTMLElement | undefined; if (!element) return; const hideShadows = !showScrollIndicators.value; if (hideShadows) { element.parentElement?.style.removeProperty("--scrollbar-width"); element.parentElement?.style.removeProperty("--scrollbar-height"); delete element.dataset.scrollTop; delete element.dataset.scrollBottom; delete element.dataset.scrollRight; delete element.dataset.scrollLeft; return; } const pufferPx = 5; // check if vertical scrolling exists if (element.scrollHeight > element.clientHeight) { // check if scrolling downwards is possible if (element.scrollHeight - element.clientHeight - pufferPx > element.scrollTop) { element.dataset.scrollBottom = ""; } else { delete element.dataset.scrollBottom; } // check if scrolling upwards is possible if (element.scrollTop - pufferPx > 0) { element.dataset.scrollTop = ""; } else { delete element.dataset.scrollTop; } // save scrollbar width in parent element as css variable element.parentElement?.style.setProperty( "--scrollbar-width", `${element.offsetWidth - element.clientWidth}px`, ); } else { // cleanup previous values element.parentElement?.style.removeProperty("--scrollbar-width"); delete element.dataset.scrollTop; delete element.dataset.scrollBottom; } // check if horizontal scrolling exists if (element.scrollWidth > element.clientWidth) { // check if scrolling rightwards is possible if (element.scrollWidth - element.clientWidth - pufferPx > element.scrollLeft) { element.dataset.scrollRight = ""; } else { delete element.dataset.scrollRight; } // check if scrolling leftwards is possible if (element.scrollLeft - pufferPx > 0) { element.dataset.scrollLeft = ""; } else { delete element.dataset.scrollLeft; } // save scrollbar height in parent element as css variable element.parentElement?.style.setProperty( "--scrollbar-height", `${element.offsetHeight - element.clientHeight}px`, ); } else { // cleanup previous values delete element.dataset.scrollRight; delete element.dataset.scrollLeft; element.parentElement?.style.removeProperty("--scrollbar-height"); } }; const updateSetScrollPossibiltiesClasses = throttle(function () { setScrollPossibilitiesClasses(refElement.value); }, 15); const refElementResizeObserver = new ResizeObserver(() => { nextTick().then(updateSetScrollPossibiltiesClasses); }); watch( () => showScrollIndicators, (shouldShowScrollIndicators) => { if (shouldShowScrollIndicators) return; refElement.value?.parentElement?.style.removeProperty("--scrollbar-width"); refElement.value?.parentElement?.style.removeProperty("--scrollbar-height"); delete refElement.value?.dataset.scrollTop; delete refElement.value?.dataset.scrollBottom; delete refElement.value?.dataset.scrollRight; delete refElement.value?.dataset.scrollLeft; }, { immediate: true, }, ); onUpdated(() => { nextTick().then(updateSetScrollPossibiltiesClasses); }); onMounted(() => { window.addEventListener("resize", updateSetScrollPossibiltiesClasses); if (!refElement.value) { return; } refElement.value.addEventListener("scroll", updateSetScrollPossibiltiesClasses); refElementResizeObserver.observe(refElement.value); nextTick().then(() => { updateSetScrollPossibiltiesClasses(); }); }); onBeforeUnmount(() => { window.removeEventListener("resize", updateSetScrollPossibiltiesClasses); if (!refElement.value) { return; } refElement.value.removeEventListener("scroll", updateSetScrollPossibiltiesClasses); refElementResizeObserver.unobserve(refElement.value); }); return {}; }