90 lines
2.7 KiB
JavaScript
90 lines
2.7 KiB
JavaScript
// Utilities
|
|
import { computed, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue';
|
|
import { clamp, consoleWarn, propsFactory } from "../util/index.mjs"; // Types
|
|
// Composables
|
|
export const makeScrollProps = propsFactory({
|
|
scrollTarget: {
|
|
type: String
|
|
},
|
|
scrollThreshold: {
|
|
type: [String, Number],
|
|
default: 300
|
|
}
|
|
}, 'scroll');
|
|
export function useScroll(props) {
|
|
let args = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
const {
|
|
canScroll
|
|
} = args;
|
|
let previousScroll = 0;
|
|
const target = ref(null);
|
|
const currentScroll = shallowRef(0);
|
|
const savedScroll = shallowRef(0);
|
|
const currentThreshold = shallowRef(0);
|
|
const isScrollActive = shallowRef(false);
|
|
const isScrollingUp = shallowRef(false);
|
|
const scrollThreshold = computed(() => {
|
|
return Number(props.scrollThreshold);
|
|
});
|
|
|
|
/**
|
|
* 1: at top
|
|
* 0: at threshold
|
|
*/
|
|
const scrollRatio = computed(() => {
|
|
return clamp((scrollThreshold.value - currentScroll.value) / scrollThreshold.value || 0);
|
|
});
|
|
const onScroll = () => {
|
|
const targetEl = target.value;
|
|
if (!targetEl || canScroll && !canScroll.value) return;
|
|
previousScroll = currentScroll.value;
|
|
currentScroll.value = 'window' in targetEl ? targetEl.pageYOffset : targetEl.scrollTop;
|
|
isScrollingUp.value = currentScroll.value < previousScroll;
|
|
currentThreshold.value = Math.abs(currentScroll.value - scrollThreshold.value);
|
|
};
|
|
watch(isScrollingUp, () => {
|
|
savedScroll.value = savedScroll.value || currentScroll.value;
|
|
});
|
|
watch(isScrollActive, () => {
|
|
savedScroll.value = 0;
|
|
});
|
|
onMounted(() => {
|
|
watch(() => props.scrollTarget, scrollTarget => {
|
|
const newTarget = scrollTarget ? document.querySelector(scrollTarget) : window;
|
|
if (!newTarget) {
|
|
consoleWarn(`Unable to locate element with identifier ${scrollTarget}`);
|
|
return;
|
|
}
|
|
if (newTarget === target.value) return;
|
|
target.value?.removeEventListener('scroll', onScroll);
|
|
target.value = newTarget;
|
|
target.value.addEventListener('scroll', onScroll, {
|
|
passive: true
|
|
});
|
|
}, {
|
|
immediate: true
|
|
});
|
|
});
|
|
onBeforeUnmount(() => {
|
|
target.value?.removeEventListener('scroll', onScroll);
|
|
});
|
|
|
|
// Do we need this? If yes - seems that
|
|
// there's no need to expose onScroll
|
|
canScroll && watch(canScroll, onScroll, {
|
|
immediate: true
|
|
});
|
|
return {
|
|
scrollThreshold,
|
|
currentScroll,
|
|
currentThreshold,
|
|
isScrollActive,
|
|
scrollRatio,
|
|
// required only for testing
|
|
// probably can be removed
|
|
// later (2 chars chlng)
|
|
isScrollingUp,
|
|
savedScroll
|
|
};
|
|
}
|
|
//# sourceMappingURL=scroll.mjs.map
|