@@ -19,6 +19,7 @@ export const initNavigation = () => {
1919 handleMobileDropdowns ( ) ;
2020 handleSearch ( ) ;
2121 handleMiniCartPosition ( ) ;
22+ handleMiniCartMobileToggle ( ) ;
2223 window . HFG . initSearch = function ( ) {
2324 handleSearch ( ) ;
2425 handleMobileDropdowns ( ) ;
@@ -224,20 +225,96 @@ function handleSearch() {
224225 * Handle the mini cart position in nav.
225226 */
226227function handleMiniCartPosition ( ) {
227- const item = document . querySelector ( '.header--row .menu-item-nav-cart' ) ;
228- if ( item === null ) {
228+ const items = document . querySelectorAll ( '.header--row .menu-item-nav-cart' ) ;
229+ if ( items . length === 0 ) {
229230 return ;
230231 }
231- const miniCart = item . querySelector ( '.nv-nav-cart:not(.cart-off-canvas)' ) ;
232232
233- if ( miniCart !== null ) {
234- miniCart . style . left =
235- item . getBoundingClientRect ( ) . left < 350 ? 0 : null ;
236- }
233+ const isMobile = window . matchMedia ( '(max-width: 959px)' ) . matches ;
234+ const sideSpacing = 2 * 16 ;
235+
236+ items . forEach ( ( item ) => {
237+ const miniCart = item . querySelector (
238+ '.nv-nav-cart:not(.cart-off-canvas)'
239+ ) ;
240+
241+ if ( miniCart === null ) {
242+ return ;
243+ }
244+
245+ miniCart . style . left = '' ;
246+ miniCart . style . right = '' ;
247+
248+ if ( isMobile ) {
249+ const cartWidth = Math . min ( 360 , window . innerWidth - sideSpacing ) ;
250+ const itemOffset = item . getBoundingClientRect ( ) . left ;
251+
252+ miniCart . style . width = `${ cartWidth } px` ;
253+ miniCart . style . maxWidth = `calc(100vw - ${ sideSpacing } px)` ;
254+ miniCart . style . left = `${
255+ ( window . innerWidth - cartWidth ) / 2 - itemOffset
256+ } px`;
257+ miniCart . style . right = 'auto' ;
258+ return ;
259+ }
260+
261+ miniCart . style . width = '' ;
262+ miniCart . style . maxWidth = '' ;
263+ miniCart . style . left = item . getBoundingClientRect ( ) . left < 350 ? 0 : '' ;
264+ } ) ;
237265}
238266
239267window . addEventListener ( 'resize' , handleMiniCartPosition ) ;
240268
269+ /**
270+ * Toggle the dropdown mini cart on tap for mobile.
271+ *
272+ * On desktop the dropdown mini cart opens on hover. Touch devices have no
273+ * hover, so without this the cart icon would just follow its link to the cart
274+ * page. Below the laptop breakpoint we toggle a `cart-dropdown-open` class on
275+ * tap instead; the dropdown's appearance is reused from the desktop styles
276+ * (see the woocommerce nav-cart styles), so customers can preview the cart and
277+ * keep shopping. It closes on a second tap or when tapping outside of it.
278+ */
279+ function handleMiniCartMobileToggle ( ) {
280+ const carts = document . querySelectorAll ( '.responsive-nav-cart.dropdown' ) ;
281+ if ( carts . length === 0 ) {
282+ return ;
283+ }
284+
285+ // Mirrors the $laptop (960px) breakpoint where the hover dropdown applies.
286+ const isMobile = ( ) => window . matchMedia ( '(max-width: 959px)' ) . matches ;
287+
288+ carts . forEach ( ( cart ) => {
289+ const openButton = cart . querySelector ( '.cart-icon-wrapper' ) ;
290+ if ( openButton === null ) {
291+ return ;
292+ }
293+ openButton . addEventListener ( 'click' , function ( e ) {
294+ if ( ! isMobile ( ) || cart . classList . contains ( 'cart-is-empty' ) ) {
295+ return ;
296+ }
297+ e . preventDefault ( ) ;
298+ cart . classList . toggle ( 'cart-dropdown-open' ) ;
299+ } ) ;
300+ } ) ;
301+
302+ // Close an open dropdown when tapping outside of it.
303+ document . addEventListener ( 'click' , function ( e ) {
304+ if ( ! isMobile ( ) ) {
305+ return ;
306+ }
307+ carts . forEach ( ( cart ) => {
308+ if (
309+ cart . classList . contains ( 'cart-dropdown-open' ) &&
310+ ! cart . contains ( e . target )
311+ ) {
312+ cart . classList . remove ( 'cart-dropdown-open' ) ;
313+ }
314+ } ) ;
315+ } ) ;
316+ }
317+
241318/**
242319 * Create an overlay to allow closing.
243320 *
0 commit comments