Untitled
/** * Stores only the original vertical position of each header cell. * If the screen width is below 992px, resets the header tile positions. */ const initializeHeaderTilePositions = () => { if (!window.matchMedia('(min-width: 992px)').matches) { resetHeaderTilePositions(); return; } const headerCells = document.querySelectorAll('thead .bundle-table__wrapper--cell'); if (!headerCells.length) return; headerCells.forEach(headerCell => { const cellRect = headerCell.getBoundingClientRect(); headerCell.dataset.originalTop = Math.round(cellRect.top + window.scrollY); headerCell.style.setProperty('--fixed-header-height', `${Math.round(cellRect.height)}px`); }); }; /** * Removes stored originalTop positions from the dataset. */ const resetHeaderTilePositions = () => { const headerCells = document.querySelectorAll('thead .bundle-table__wrapper--cell'); if (!headerCells.length) return; headerCells.forEach(headerCell => { delete headerCell.dataset.originalTop; delete headerCell.dataset.originalBottom; headerCell.style.removeProperty('--fixed-header-height'); }); }; /** * Indicates whether the user is currently scrolling. */ let isUserScrolling = false; /** * Updates the position of header tiles for a sticky header effect. */ const updateHeaderTilePositions = () => { // Prevent reentry if already scrolling if (isUserScrolling) return; isUserScrolling = true; requestAnimationFrame(() => { const headerCells = document.querySelectorAll('thead .bundle-table__wrapper--cell'); const tableContainer = document.querySelector('.bundle-table-container'); const priceCell = document.querySelector('.bundle-table__wrapper--cell--price'); if (!headerCells.length || !tableContainer || !priceCell) { isUserScrolling = false; return; } const containerRect = tableContainer.getBoundingClientRect(); const priceRect = priceCell.getBoundingClientRect(); const priceDistanceToTop = Math.round(priceRect.top); headerCells.forEach(headerCell => { const headerTile = headerCell.querySelector('.bundle-table__header-tile'); const originalTop = parseFloat(headerCell.dataset.originalTop); if (!headerTile || isNaN(originalTop)) return; const cellRect = headerCell.getBoundingClientRect(); const cellHeight = cellRect.height; const offsetLeft = cellRect.left - containerRect.left; const shouldFixHeader = window.scrollY > originalTop; headerTile.classList.toggle('fixed', shouldFixHeader); if (shouldFixHeader) { // variable for parent left padding const LEFT_OFFSET = 6; headerTile.style.setProperty('--fixed-header-left', `${offsetLeft + LEFT_OFFSET}px`); } else { headerTile.style.removeProperty('--fixed-header-left'); } const heightDifference = priceDistanceToTop - cellHeight; if (cellHeight > priceDistanceToTop) { const fixedHeaderTop = Math.max(heightDifference, -cellHeight); headerTile.style.setProperty('--fixed-header-top', `${fixedHeaderTop}px`); } else { headerTile.style.removeProperty('--fixed-header-top'); } }); isUserScrolling = false; }); }; /** * Monitors scroll events in the table container and updates header tiles accordingly. */ const monitorTableContainerScroll = () => { const tableContainer = document.querySelector('.bundle-table-container'); if (!tableContainer) return; tableContainer.addEventListener('scroll', () => { if (window.matchMedia('(min-width: 992px)').matches) { initializeHeaderTilePositions(); updateHeaderTilePositions(); } else { resetHeaderTilePositions(); resetHeaderTileStyles(); } }); }; /** * Resets the styles applied to header tiles (removes the 'fixed' class and inline styles). */ const resetHeaderTileStyles = () => { const headerCells = document.querySelectorAll('thead .bundle-table__wrapper--cell'); if (!headerCells.length) return; headerCells.forEach(headerCell => { const headerTile = headerCell.querySelector('.bundle-table__header-tile'); if (!headerTile) return; headerTile.classList.remove('fixed'); headerTile.style.removeProperty('--fixed-header-left'); headerTile.style.removeProperty('--fixed-header-top'); }); }; /** * Handles window resize events to re-initialize or reset header tile positions/styles. */ const handleWindowResize = () => { if (window.matchMedia('(min-width: 992px)').matches) { initializeHeaderTilePositions(); updateHeaderTilePositions(); } else { resetHeaderTilePositions(); resetHeaderTileStyles(); } };
Leave a Comment