import { nextTick, unref } from "vue"; export const useScroll = () => { const resolveElement = (value) => { const instance = unref(value); return instance?.$el ?? instance ?? null; }; const resolveContainer = (targetElement, containerRef) => { const containerInstance = unref(containerRef); if (typeof containerInstance?.setScrollPosition === "function") { return { instance: containerInstance, element: containerInstance.getScrollTarget?.() ?? containerInstance.$el?.querySelector?.( ".q-scrollarea__container", ) ?? containerInstance.$el, isQuasar: true, }; } const providedElement = resolveElement(containerRef); const detectedElement = targetElement?.closest?.( ".q-scrollarea__container, .scroll, .scroll-y", ); const element = providedElement ?? detectedElement ?? document.scrollingElement ?? document.documentElement; return { instance: element, element, isQuasar: false, }; }; // const calculatePosition = (targetElement, containerElement, offset) => { const targetRect = targetElement.getBoundingClientRect(); const isDocumentContainer = containerElement === document.body || containerElement === document.documentElement || containerElement === document.scrollingElement; if (isDocumentContainer) { return Math.max( 0, targetRect.top + window.scrollY - offset, ); } const containerRect = containerElement.getBoundingClientRect(); return Math.max( 0, targetRect.top - containerRect.top + containerElement.scrollTop - offset, ); }; const scrollToComponent = (targetRef, containerRef, options = {}) => { const { offset = 150, duration = 150, behavior = "smooth" } = options; const targetElement = resolveElement(targetRef); if (!targetElement) { console.warn("useScroll: Target element not found"); return false; } const container = resolveContainer(targetElement, containerRef); const containerElement = container.element; if (!containerElement) { console.warn("useScroll: Container element not found"); return false; } const isDocumentContainer = containerElement === document.body || containerElement === document.documentElement || containerElement === document.scrollingElement; if ( !isDocumentContainer && !containerElement.contains(targetElement) ) { console.warn("useScroll: Target is not inside the container"); return false; } try { const targetPosition = calculatePosition( targetElement, containerElement, offset, ); if (container.isQuasar) { container.instance.setScrollPosition( "vertical", targetPosition, duration, ); } else if (isDocumentContainer) { window.scrollTo({ top: targetPosition, behavior, }); } else { containerElement.scrollTo({ top: targetPosition, behavior, }); } return true; } catch (error) { console.error("useScroll: Error while scrolling", error); return false; } }; const scrollToFirstError = async ( formRef, containerRef, options = {}, ) => { await nextTick(); const forms = unref(formRef); const formInstances = Array.isArray(forms) ? forms : [forms]; for (const formInstance of formInstances) { const formElement = resolveElement(formInstance); const errorElement = formElement?.matches?.(".q-field--error") ? formElement : formElement?.querySelector?.(".q-field--error"); if (errorElement) { const scrollFn = options.scrollFn ?? scrollToComponent; return scrollFn(errorElement, containerRef, options); } } return false; }; return { scrollToComponent, scrollToFirstError, }; };