UNPKG

@empathyco/x-components

Version:
137 lines (134 loc) 5.34 kB
import { defineComponent, ref, computed, onMounted, watch, onBeforeUnmount } from 'vue'; import { useDebounce } from '../composables/use-debounce.js'; /** * This component allows for any other component or element inside it to be horizontally * navigable. It also implements customizable buttons as well as other minor customizations to its * general behavior. * * Additionally, this component exposes the following props to modify the classes of the * elements: `buttonClass`. * * @public */ var _sfc_main = defineComponent({ name: 'SlidingPanel', props: { /** * Scroll factor that will dictate how much the scroll moves when pressing a navigation button. */ scrollFactor: { type: Number, default: 0.7, }, /** Would make the navigation buttons visible when they're needed or always hide them. */ showButtons: { type: Boolean, default: true, }, /** * When true, whenever the DOM content in the sliding panel slot changes, it will reset * the scroll position to 0. */ resetOnContentChange: { type: Boolean, default: true, }, buttonClass: { type: [String, Object, Array] }, scrollContainerClass: { type: [String, Object, Array] }, }, setup(props, { slots }) { /** Indicates if the scroll is at the start of the sliding panel. */ const isScrollAtStart = ref(true); /** Indicates if the scroll is at the end of the sliding panel. */ const isScrollAtEnd = ref(true); const scrollContainerRef = ref(); /** * Updates the values of the scroll positions to show or hide the buttons depending on it. * * @remarks The 2px extra is to fix some cases in some resolutions where the scroll + client * size is less than the scroll width even when the scroll is at the end. */ function updateScrollPosition() { if (scrollContainerRef.value) { const { scrollLeft, clientWidth, scrollWidth } = scrollContainerRef.value; isScrollAtStart.value = !scrollLeft; isScrollAtEnd.value = scrollLeft + clientWidth + 2 >= scrollWidth; } } /** * Debounced version of the {@link SlidingPanel.updateScrollPosition} method. */ const debouncedUpdateScroll = useDebounce(updateScrollPosition, 50, { leading: true }); /** * Resets the scroll and updates the values of the scroll for the buttons to react. */ const debouncedRestoreAndUpdateScroll = useDebounce(() => { scrollContainerRef.value.scroll({ left: 0, behavior: 'smooth' }); updateScrollPosition(); }, 50, { leading: true }); /** * Scrolls the wrapper element towards the provided scroll value. * * @param scrollValue - The value the scroll will go towards. */ function scrollTo(scrollValue) { scrollContainerRef.value.scrollBy({ left: scrollValue * props.scrollFactor, behavior: 'smooth', }); } /** Scrolls the wrapper element to the left. */ function scrollLeft() { scrollTo(-scrollContainerRef.value.clientWidth); } /** Scrolls the wrapper element to the right. */ function scrollRight() { scrollTo(scrollContainerRef.value.clientWidth); } /** CSS classes to apply based on the scroll position. */ const cssClasses = computed(() => ({ 'x-sliding-panel-at-start': isScrollAtStart.value, 'x-sliding-panel-at-end': isScrollAtEnd.value, })); let resizeObserver; let contentChangedObserver; /** * Initialises browser platform code: * - Creates a mutation observer to detect content changes and reset scroll position. * - Stores initial size and scroll position values. */ onMounted(() => { resizeObserver = new ResizeObserver(debouncedUpdateScroll); resizeObserver.observe(scrollContainerRef.value); contentChangedObserver = new MutationObserver(debouncedRestoreAndUpdateScroll); watch(() => props.resetOnContentChange, shouldReset => { if (shouldReset) { contentChangedObserver.observe(scrollContainerRef.value, { subtree: true, childList: true, attributes: false, characterData: false, }); } else { contentChangedObserver.disconnect(); } }, { immediate: true }); updateScrollPosition(); }); onBeforeUnmount(() => { contentChangedObserver.disconnect(); resizeObserver.disconnect(); }); return { cssClasses, debouncedUpdateScroll, scrollContainerRef, scrollLeft, scrollRight, slots, }; }, }); export { _sfc_main as default }; //# sourceMappingURL=sliding-panel.vue2.js.map