1/** 2 * Frontend behavior for mobile sticky location detail actions. 3 */ 4( function() { 5 'use strict'; 6 7 const MOBILE_BREAKPOINT = 1024; 8 const body = document.body; 9 const openFrames = new WeakMap(); 10 const closeTimers = new WeakMap(); 11 const closeDelay = 240; 12 13 function setModalState( modalNode, isOpen ) { 14 if ( ! modalNode ) { 15 return; 16 } 17 18 if ( closeTimers.has( modalNode ) ) { 19 window.clearTimeout( closeTimers.get( modalNode ) ); 20 closeTimers.delete( modalNode ); 21 } 22 23 if ( openFrames.has( modalNode ) ) { 24 window.cancelAnimationFrame( openFrames.get( modalNode ) ); 25 openFrames.delete( modalNode ); 26 } 27 28 if ( isOpen ) { 29 modalNode.hidden = false; 30 31 openFrames.set( modalNode, window.requestAnimationFrame( function() { 32 modalNode.classList.add( 'is-open' ); 33 openFrames.delete( modalNode ); 34 } ) ); 35 } else { 36 modalNode.classList.remove( 'is-open' ); 37 38 closeTimers.set( modalNode, window.setTimeout( function() { 39 modalNode.hidden = true; 40 closeTimers.delete( modalNode ); 41 }, closeDelay ) ); 42 } 43 44 if ( body ) { 45 body.classList.toggle( 'vb-delivery-modal-open', isOpen ); 46 } 47 } 48 49 function initDeliveryModal( scopeNode ) { 50 const modalNode = scopeNode.querySelector( '[data-vb-delivery-modal]' ); 51 const triggerNodes = scopeNode.querySelectorAll( '[data-vb-delivery-open]' ); 52 53 if ( ! modalNode || ! triggerNodes.length ) { 54 return; 55 } 56 57 if ( modalNode.parentElement !== document.body ) { 58 document.body.appendChild( modalNode ); 59 } 60 61 const closeNodes = modalNode.querySelectorAll( '[data-vb-delivery-close]' ); 62 const optionNodes = modalNode.querySelectorAll( 'a' ); 63 64 triggerNodes.forEach( function( triggerNode ) { 65 triggerNode.addEventListener( 'click', function() { 66 setModalState( modalNode, true ); 67 } ); 68 } ); 69 70 closeNodes.forEach( function( closeNode ) { 71 closeNode.addEventListener( 'click', function() { 72 setModalState( modalNode, false ); 73 } ); 74 } ); 75 76 optionNodes.forEach( function( optionNode ) { 77 optionNode.addEventListener( 'click', function() { 78 setModalState( modalNode, false ); 79 } ); 80 } ); 81 82 modalNode.addEventListener( 'click', function( event ) { 83 if ( event.target === modalNode ) { 84 setModalState( modalNode, false ); 85 } 86 } ); 87 88 document.addEventListener( 'keydown', function( event ) { 89 if ( 'Escape' === event.key && ! modalNode.hidden ) { 90 setModalState( modalNode, false ); 91 } 92 } ); 93 } 94 95 function setStickyState( stickyNode, isVisible ) { 96 stickyNode.classList.toggle( 'is-visible', isVisible ); 97 stickyNode.setAttribute( 'aria-hidden', isVisible ? 'false' : 'true' ); 98 99 // Body-class zodat we los-in-de-body geplaatste widgets (zoals #gifty-container) kunnen 100 // verbergen zolang de sticky-actiebalk in beeld is. 101 if ( body ) { 102 body.classList.toggle( 'vb-location-sticky-visible', isVisible ); 103 } 104 } 105 106 function initBlock( block ) { 107 const sourceNode = block.querySelector( '[data-vb-location-detail-actions-source]' ); 108 const stickyNode = block.querySelector( '[data-vb-location-detail-actions-sticky]' ); 109 const footerNode = document.querySelector( 'footer' ); 110 111 if ( ! sourceNode || ! stickyNode ) { 112 return; 113 } 114 115 const entryContent = 116 block.closest( '.entry-content' ) || 117 block.closest( '.wp-block-post-content' ) || 118 block.parentElement; 119 120 if ( entryContent && stickyNode.parentElement !== entryContent ) { 121 entryContent.appendChild( stickyNode ); 122 } 123 124 initDeliveryModal( entryContent || block ); 125 126 let ticking = false; 127 128 function updateSticky() { 129 ticking = false; 130 131 if ( window.innerWidth > MOBILE_BREAKPOINT ) { 132 setStickyState( stickyNode, false ); 133 return; 134 } 135 136 const rect = sourceNode.getBoundingClientRect(); 137 const isPastSource = rect.bottom <= 0; 138 let isAboveFooter = false; 139 140 if ( footerNode ) { 141 const footerRect = footerNode.getBoundingClientRect(); 142 const stickyBottom = parseFloat( window.getComputedStyle( stickyNode ).bottom || '18' ) || 18; 143 const stickyHeight = stickyNode.offsetHeight || 0; 144 const stickyTopThreshold = window.innerHeight - stickyBottom - stickyHeight - 8; 145 146 isAboveFooter = footerRect.top <= stickyTopThreshold; 147 } 148 149 setStickyState( stickyNode, isPastSource && ! isAboveFooter ); 150 } 151 152 function requestUpdate() { 153 if ( ticking ) { 154 return; 155 } 156 157 ticking = true; 158 window.requestAnimationFrame( updateSticky ); 159 } 160 161 requestUpdate(); 162 window.addEventListener( 'scroll', requestUpdate, { passive: true } ); 163 window.addEventListener( 'resize', requestUpdate ); 164 } 165 166 document.addEventListener( 'DOMContentLoaded', function() { 167 document.querySelectorAll( '[data-vb-location-detail-actions]' ).forEach( initBlock ); 168 } ); 169} )();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.