UNPKG

@ulu/frontend

Version:

A framework-agnostic frontend toolkit providing a modular, tree-shakable library of accessible components and utilities. Designed for seamless integration, it features a highly configurable SCSS system for any environment and vanilla JavaScript modules op

74 lines (69 loc) 3.53 kB
/** * @module ui/overflow-scroller-pager */ /** * Function to be used in overflow scrollers "amount" option. This function will * determine how many items can fit in the viewport, taking into account scroll padding left, * and will set the scroll amount to paginate between items. Items size can be anything * (ie. one per screen vs 3.5 per screen will both work). This seperated from the plugin * for tree shaking incase it's unneeded. Currently this is only setup for horizontal scrolling * * Note: This is setup to return the function, incase configuration is needed in the future * it can be passed to the create function * * @return {Function} A function to be used in overflow scrollers "amount" configuration property */ export function createPager() { return function pager(instance, dir) { const isNext = dir === "next"; const { track } = instance.elements; if (!track.children) return 400; const trackStyle = window.getComputedStyle(track); // Note we are banking on the scroll padding string to be in "px", not doing any conversions here const scrollPaddingRaw = trackStyle.getPropertyValue( 'scroll-padding-left' ).replace( 'auto', '0px' ); const scrollPadding = parseInt(scrollPaddingRaw, 10) || 0; const { scrollLeft, offsetWidth } = track; const right = scrollLeft + offsetWidth; // Get all slide positions into an array const slides = [ ...track.children ].map(element => { const { offsetLeft, offsetWidth } = element; return { element, offsetLeft, offsetRight: offsetLeft + offsetWidth }; }); // Test edges to see what can fit, slide found will be the slide to scroll to let slideFound; if (isNext) { // Find the first item whose right edge is cut off by the right side of the viewport. // -1 handles sub-pixel rounding issues. slideFound = slides.find(slide => slide.offsetRight > right - 1); } else { // Find the index of the slide immediately before the currently visible (padded) area. // -1 ensures we grab the slide that is fully outside the view. let slideBeforeIndex = slides.findLastIndex(slide => slide.offsetLeft < scrollLeft + scrollPadding - 1); // Find the slides before this slide that can fit, including the slide before if (slideBeforeIndex > -1) { let slideBefore = slides[slideBeforeIndex]; let slidesBefore = slides.slice(0, slideBeforeIndex + 1); // Iterate backwards from the start to see how far back we can jump while still // keeping our anchor `slideBefore` fully visible in the new viewport. slideFound = slidesBefore.find(slide => { // Calculate where the right edge of the viewport will be if we snap to this slide. // Snapping aligns slide.offsetLeft to the padding. const rightEdge = slide.offsetLeft - scrollPadding + offsetWidth; // Return true if our anchor slide is still within this new right edge. return rightEdge >= slideBefore.offsetRight - 1; }) || slideBefore; // Fallback to `slideBefore` if no older slides fit in the same view. } } if (slideFound) { // Return the target scroll position that aligns the target slide's left edge // exactly with the CSS scroll-padding. Ensure we don't return a negative scroll amount. return Math.max(0, slideFound.offsetLeft - scrollPadding); } else { return 400; // Fallback amount } } }