This commit represents the complete foundational codebase for Nimbus Alpha, a Laravel package that provides an integrated, in-browser API client with automatic schema discovery from validation rules. IMPORTANT: This is a squashed commit representing the culmination of extensive development, refactoring, and architectural iterations. All previous commit history has been intentionally removed to provide a clean foundation for the public alpha release. The development of Nimbus involved: - Multiple architectural refactorings - Significant structural changes - Experimental approaches that were later abandoned - Learning iterations on the core concept - Migration between different design patterns This messy history would: - Make git blame confusing and unhelpful - Obscure the actual intent behind current implementation - Create noise when reviewing changes - Reference deleted or refactored code If git blame brought you to this commit, it means you're looking at code that was part of the initial alpha release. Here's what to do: 1. Check Current Documentation - See `/wiki/contribution-guide/README.md` for architecture details - Review the specific module's README if available - Look for inline comments explaining the reasoning 2. Look for Related Code - Check other files in the same module - Look for tests that demonstrate intended behavior - Review interfaces and contracts 3. Context Matters - This code may have been updated since alpha - Check git log for subsequent changes to this file - Look for related issues or PRs on GitHub --- This commit marks the beginning of Nimbus's public journey. All future commits will build upon this foundation with clear, traceable history. Thank you for using or contributing to Nimbus!
182 lines
4.7 KiB
TypeScript
182 lines
4.7 KiB
TypeScript
import {
|
|
tabNavigationScrollConfig,
|
|
TabNavigationScrollConfig,
|
|
} from '@/config/tab-navigation-scroll';
|
|
import {
|
|
calculateScrollToElement,
|
|
getElementVisibility,
|
|
getMaskVisibility,
|
|
getScrollBounds,
|
|
} from '@/utils/scroll';
|
|
import { useDebounceFn } from '@vueuse/core';
|
|
import { computed, nextTick, onMounted, onUnmounted, ref } from 'vue';
|
|
|
|
/**
|
|
* Manages horizontal scroll state and behavior for tab navigation.
|
|
*
|
|
* Provides reactive scroll management with configurable behavior and state management.
|
|
*/
|
|
export function useTabHorizontalScroll(config?: Partial<TabNavigationScrollConfig>) {
|
|
/*
|
|
* Configuration.
|
|
*
|
|
* Note: variables are assigned explicitly to make the config keys referencable.
|
|
*/
|
|
|
|
const maskWidth = config?.MASK_WIDTH ?? tabNavigationScrollConfig.MASK_WIDTH;
|
|
|
|
const scrollThreshold =
|
|
config?.SCROLL_THRESHOLD ?? tabNavigationScrollConfig.SCROLL_THRESHOLD;
|
|
|
|
const scrollPadding =
|
|
config?.SCROLL_PADDING ?? tabNavigationScrollConfig.SCROLL_PADDING;
|
|
|
|
const animationDuration =
|
|
config?.ANIMATION_DURATION ?? tabNavigationScrollConfig.ANIMATION_DURATION;
|
|
|
|
const debounceDelay =
|
|
config?.DEBOUNCE_DELAY ?? tabNavigationScrollConfig.DEBOUNCE_DELAY;
|
|
|
|
/*
|
|
* State.
|
|
*/
|
|
|
|
const scrollContainer = ref<HTMLElement | null>(null);
|
|
const showLeftMask = ref(false);
|
|
const showRightMask = ref(false);
|
|
const savedScrollPosition = ref(0);
|
|
|
|
/*
|
|
* Computed.
|
|
*/
|
|
|
|
const scrollBounds = computed(() =>
|
|
scrollContainer.value
|
|
? getScrollBounds(scrollContainer.value, scrollThreshold)
|
|
: null,
|
|
);
|
|
|
|
/*
|
|
* Actions.
|
|
*/
|
|
|
|
/**
|
|
* Updates gradient mask visibility based on scroll position
|
|
*
|
|
* Uses debounced updates for better performance during scroll events.
|
|
*/
|
|
const updateScrollMasks = () => {
|
|
if (!scrollContainer.value) {
|
|
return;
|
|
}
|
|
|
|
const bounds = getScrollBounds(scrollContainer.value, scrollThreshold);
|
|
const maskVisibility = getMaskVisibility(bounds);
|
|
|
|
showLeftMask.value = maskVisibility.showLeftMask;
|
|
showRightMask.value = maskVisibility.showRightMask;
|
|
savedScrollPosition.value = bounds.current;
|
|
};
|
|
|
|
/**
|
|
* Debounced version of updateScrollMasks for scroll events
|
|
*/
|
|
const debouncedUpdateMasks = useDebounceFn(updateScrollMasks, debounceDelay);
|
|
|
|
/**
|
|
* Scrolls a tab button into view, accounting for gradient masks
|
|
*
|
|
* Uses extracted calculation utilities for cleaner logic and better maintainability.
|
|
*/
|
|
const scrollTabIntoView = (buttonElement: HTMLElement) => {
|
|
if (!scrollContainer.value) {
|
|
return;
|
|
}
|
|
|
|
const visibility = getElementVisibility(
|
|
buttonElement,
|
|
scrollContainer.value,
|
|
maskWidth,
|
|
);
|
|
|
|
if (!visibility.isFullyVisible) {
|
|
const targetScrollLeft = calculateScrollToElement(
|
|
buttonElement,
|
|
scrollContainer.value,
|
|
maskWidth,
|
|
scrollPadding,
|
|
);
|
|
|
|
scrollContainer.value.scrollTo({
|
|
left: targetScrollLeft,
|
|
behavior: 'smooth',
|
|
});
|
|
|
|
// Update masks after scrolling animation completes
|
|
setTimeout(() => {
|
|
updateScrollMasks();
|
|
}, animationDuration);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Restores the previously saved scroll position when menu reopens
|
|
*/
|
|
const restoreScrollPosition = async () => {
|
|
await nextTick();
|
|
|
|
if (scrollContainer.value && savedScrollPosition.value > 0) {
|
|
scrollContainer.value.scrollLeft = savedScrollPosition.value;
|
|
}
|
|
|
|
updateScrollMasks();
|
|
};
|
|
|
|
/**
|
|
* Sets up scroll event listeners
|
|
*/
|
|
const setupScrollListeners = () => {
|
|
if (!scrollContainer.value) {
|
|
return;
|
|
}
|
|
|
|
scrollContainer.value.addEventListener('scroll', debouncedUpdateMasks, {
|
|
passive: true,
|
|
});
|
|
|
|
return () => {
|
|
scrollContainer.value?.removeEventListener('scroll', debouncedUpdateMasks);
|
|
};
|
|
};
|
|
|
|
/*
|
|
* Lifecycle.
|
|
*/
|
|
|
|
let cleanupScrollListeners: (() => void) | null = null;
|
|
|
|
onMounted(() => {
|
|
cleanupScrollListeners = setupScrollListeners() ?? null;
|
|
});
|
|
|
|
onUnmounted(() => {
|
|
cleanupScrollListeners?.();
|
|
savedScrollPosition.value = 0;
|
|
});
|
|
|
|
return {
|
|
// State
|
|
|
|
scrollContainer,
|
|
showLeftMask,
|
|
showRightMask,
|
|
scrollBounds,
|
|
|
|
// Actions
|
|
|
|
updateScrollMasks,
|
|
scrollTabIntoView,
|
|
restoreScrollPosition,
|
|
};
|
|
}
|