<button class="header-flyout__toggle header-flyout__toggle--cart
   header-flyout__toggle--with-fallback
   js-header-flyout-icon-empty-cart
  js-header-icon--cart" id="header-flyout-cart-toggle" aria-controls="header-flyout-cart" aria-expanded="false" data-header-flyout data-header-flyout-type="cart">
  <span class="u-visually-hidden">Cart</span>
    <svg class="icon icon--cart">
  <use xlink:href="/assets/icons/icons.svg#cart"></use>
</svg>

    <svg class="icon icon--cart-item">
  <use xlink:href="/assets/icons/icons.svg#cart-item"></use>
</svg>

</button>

<a href="#" class="header-flyout__link header-flyout__link--cart
    ">
    <span class="u-visually-hidden">Cart</span>
      <svg class="icon icon--cart">
  <use xlink:href="/assets/icons/icons.svg#cart"></use>
</svg>

      <svg class="icon icon--cart-item">
  <use xlink:href="/assets/icons/icons.svg#cart-item"></use>
</svg>

  </a>

<div class="header-flyout header-flyout--cart
   header-flyout--with-fallback" id="header-flyout-cart" aria-hidden="true">
    <button aria-label="Close Cart" class="header-flyout__close js-header-cart-close-button">
    <svg class="icon icon--close-large">
  <use xlink:href="/assets/icons/icons.svg#close-large"></use>
</svg>

  </button>
    <div class="header-flyout__header">
        <span class="header-cart__title">Your Cart <svg class="icon icon--arrow-right">
  <use xlink:href="/assets/icons/icons.svg#arrow-right"></use>
</svg>
</span>
    </div>
    <div class="header-flyout__content">
        <div class="header-cart header-cart--zeal--empty-cart header-cart--tax header-cart--express ">
            <div class="header-cart__empty">
                <div class="header-cart__empty-inner">
                    <img class="header-cart__empty-brand-logo" src="/assets/images/zeal-brand-mark-grey-small.png">
                    <span class="header-cart__empty-label">Your Shopping Cart is Empty</span>
                    <a class="header-cart__empty-button button button--primary" href="#">Shop Now</a>
                </div>
            </div>
        </div>

    </div>
</div>

<div class="header-flyout__overlay header-flyout__overlay--cart js-header-flyout-overlay js-header-flyout-overlay--cart"></div>
<button class="header-flyout__toggle header-flyout__toggle--{{type}}
  {{#if linkFallback}} header-flyout__toggle--with-fallback{{/if}}
  {{#if altIcon}} js-header-flyout-icon-empty-{{type}}{{/if}}
  js-header-icon--{{type}}" id="header-flyout-{{type}}-toggle"
  aria-controls="header-flyout-{{type}}" aria-expanded="false"
  data-header-flyout data-header-flyout-type="{{type}}">
  <span class="u-visually-hidden">{{buttonLabel}}</span>
  {{#each icons}}
    {{render (dynamicComponent this)}}
  {{/each}}
  {{#if badge}}
    <span class="header-flyout__badge">{{badge}}</span>
  {{/if}}
</button>

{{!--
  Some flyouts (e.g. cart) should only appear in certain breakpoints. For those
  cases, a link may be rendered as an alternative.
--}}
{{#if linkFallback}}
  <a href="#" class="header-flyout__link header-flyout__link--{{type}}
    {{#unless altIcon}} js-header-icon-empty-{{type}}{{/unless}}">
    <span class="u-visually-hidden">{{buttonLabel}}</span>
    {{#each icons}}
      {{render (dynamicComponent this)}}
    {{/each}}
    {{#if badge}}
      <span class="header-flyout__badge">{{badge}}</span>
    {{/if}}
  </a>
{{/if}}

<div class="header-flyout header-flyout--{{type}}
  {{#if linkFallback}} header-flyout--with-fallback{{/if}}"
  id="header-flyout-{{type}}" aria-hidden="true">
  <button aria-label="Close Cart" class="header-flyout__close js-header-cart-close-button">
    {{render '@icons--close-large'}}
  </button>
  {{#if heading}}
    <div class="header-flyout__header">
      <span class="header-cart__title">{{heading}} {{render '@icons--arrow-right'}}</span>
    </div>
  {{/if}}
  <div class="header-flyout__content">
    {{render (dynamicComponent content)}}
  </div>
</div>

<div class="header-flyout__overlay header-flyout__overlay--{{type}} js-header-flyout-overlay js-header-flyout-overlay--{{type}}"></div>
{
  "type": "cart",
  "buttonLabel": "Cart",
  "linkFallback": true,
  "icons": [
    "icons--cart",
    "icons--cart-item"
  ],
  "heading": "Your Cart",
  "altIcon": true,
  "content": "mini-cart--zeal--empty-cart"
}
  • Content:
    (function (window) {
      'use strict';
    
      const header = document.querySelector('.header');
      const headroom = document.querySelector('.headroom');
      const toggles = document.querySelectorAll('[data-header-flyout]');
      const flyout = document.querySelector('.js-header-cart');
      const cartIcon = document.querySelector('.js-header-icon--cart');
      const cartCloseButton = document.querySelector('.js-header-cart-close-button');
      const flyoutOverlay = document.querySelector('.js-header-flyout-overlay');
      const cartOpenClass = 'js-header-cart-open';
    
      // Events are emitted for flyout actions so that implementation JS can react
      // as needed.
      function emitFlyoutEvent(flyoutId, action) {
        componentEvents.emitEvent('flyout-change', [{
          id: flyoutId,
          action: action
        }]);
      }
    
      // Allows implementations to toggle whether the cart icon in the header reads
      // as empty or full. Needed as items are added/removed from the cart via Ajax.
      // To-do: implement this as a generic alt icon (i.e. not cart-specific).
      function handleToggleIconEvent(toggle, args) {
        if (args && args.action) {
          const type = toggle.dataset.headerFlyoutType;
          const iconSelectors = [
            '.header-flyout__link--' + type,
            '.header-flyout__button--' + type,
          ];
          const icons = document.querySelectorAll(iconSelectors.join(','));
    
          Array.prototype.forEach(icons, function (icon) {
            if (args.action == 'full') {
              icon.classList.remove('js-header-flyout-icon-empty-' + type);
            }
            else if (args.action == 'empty') {
              icon.classList.add('js-header-flyout-icon-empty-cart');
            }
          });
        }
      }
    
      function toggleFlyoutVisibility(event) {
        const toggle = event.currentTarget;
        const type = toggle.dataset.headerFlyoutType;
    
        if (toggle.getAttribute('aria-expanded') === 'true') {
          closeFlyout(toggle);
        }
        else {
          openFlyout(toggle);
        }
      }
    
      function openFlyout(toggle) {
        const type = toggle.dataset.headerFlyoutType;
        const flyout = document.getElementById(toggle.getAttribute('aria-controls'));
        if (flyout) {
          closeAllFlyouts();
    
          // iOS requires `body { position: fixed }` to prevent background scrolling
          // while the modal is open, which forces the page to jump to the top. The
          // following counteracts that jump. closeFlyout() resets this.
          document.body.dataset.previousScrollTop = (document.documentElement.scrollTop || document.body.scrollTop);
          document.body.style.top = (-1 * document.body.dataset.previousScrollTop) + 'px';
    
          document.body.classList.add('js-header-flyout-' + type + '-open');
    
          // Handle keydown events to trap focus.
          flyout.addEventListener('keydown', handleKeydown);
    
          const flyoutClose = flyout.querySelector('.header-flyout__close');
          if (flyoutClose) {
            // Focus on close button.
            flyoutClose.focus();
          }
    
          toggle.setAttribute('aria-expanded', true);
          flyout.setAttribute('aria-hidden', false);
    
          emitFlyoutEvent(type, 'open');
        }
      }
    
      function closeFlyout(toggle, focusBackOnToggle = false) {
        // Since search flyout calls this and not all countries get a cart, check
        // that it exists before proceeding.
        const type = toggle.dataset.headerFlyoutType;
        const flyout = document.getElementById(toggle.getAttribute('aria-controls'));
        if (flyout) {
          // Return the body to its original scroll position before modal was open.
          document.documentElement.scrollTop = document.body.scrollTop = document.body.dataset.previousScrollTop;
          delete document.body.style.top;
          delete document.body.dataset.previousScrollTop;
    
          document.body.classList.remove('js-header-flyout-' + type + '-open');
    
          // Remove focus trap.
          flyout.removeEventListener('keydown', handleKeydown);
    
          if (focusBackOnToggle) {
            toggle.focus();
          }
    
          toggle.setAttribute('aria-expanded', false);
          flyout.setAttribute('aria-hidden', true);
    
          emitFlyoutEvent(type, 'close');
        }
      }
    
      function closeAllFlyouts() {
        // Find all open flyouts and close them.
        const openToggles = document.querySelectorAll('[data-header-flyout][aria-expanded=true]');
        if (openToggles.length) {
          Array.prototype.forEach.call(openToggles, closeFlyout);
        }
      }
    
      // Use global modal keydown handler.
      function handleKeydown(event) {
        const toggle = document.getElementById(event.currentTarget.dataset.headerFlyoutToggle);
        const closeFunc = closeFlyout.bind(null, toggle, true);
        handleModalKeydown(event, closeFunc);
      }
    
      function closeFlyoutAndFocus(toggle) {
        closeFlyout(toggle, true);
      }
    
      function initFlyoutOverlays() {
        // Attach listener to the document in case the overlay element itself is
        // refreshed or injected via Ajax for some reason.
        document.addEventListener('click', function (event) {
          // Only act if target is flyout overlay.
          if (!event.target.classList.contains('js-header-flyout-overlay')) return;
    
          closeAllFlyouts();
        });
      }
    
      // Icons may be replaced in-line after the initial pageload so they can be
      // user-specific on a cached page, so we query the DOM for them again.
      function initToggleAndFlyout(toggle) {
        const flyout = document.getElementById(toggle.getAttribute('aria-controls'));
        if (flyout && !toggle.classList.contains('js-component-init')) {
          // Store reference to toggle in flyout data attr for convenience.
          flyout.dataset.headerFlyoutToggle = toggle.id;
    
          // Handle clicks on close button within flyout.
          flyout.addEventListener('click', function (event) {
            if (event.target.classList.contains('header-flyout__close')) {
              closeFlyoutAndFocus(toggle);
            }
          });
    
          toggle.addEventListener('click', toggleFlyoutVisibility);
          toggle.classList.add('js-component-init');
        }
      }
    
      if (toggles && toggles.length) {
        // Allow application JS to reinitialize any instances added with Ajax, etc.
        Array.prototype.forEach.call(toggles, function (toggle) {
          const type = toggle.dataset.headerFlyoutType;
          if (typeof componentEvents !== 'undefined') {
            componentEvents.on('open-' + type + '-flyout',
              openFlyout.bind(null, toggle));
            componentEvents.on('close-' + type + '-flyout',
              closeFlyout.bind(null, toggle));
            componentEvents.on('change-' + type + '-icon',
              handleToggleIconEvent.bind(null, toggle));
          }
          initToggleAndFlyout(toggle);
        });
    
        if (typeof componentEvents !== 'undefined') {
          componentEvents.on('close-all-flyouts', closeAllFlyouts);
        }
    
        initFlyoutOverlays();
      }
    })(this);
    
  • URL: /components/raw/header-flyout/header-flyout.js
  • Filesystem Path: src/components/02-components/header-flyout/header-flyout.js
  • Size: 6.7 KB
  • Content:
    // Note: This entire stylesheet is served as critical CSS.
    /* critical:start */
    .header-flyout {
      display: none;
      position: fixed;
      top: $header-sm-height-total;
      right: 0;
      left: 0;
      background-color: $color-white;
      box-shadow: $header-dropshadow;
      z-index: 12;
      box-sizing: border-box;
    
      @include breakpoint($breakpoint-sm-only) {
        .header-no-primary-links & {
          top: 50px;
        }
    
        &--with-fallback {
          display: none;
        }
    
        @if $brand == 'b2b' {
          height: calc(100vh - #{$header-sm-height-total});
        }
      }
    
      @include breakpoint($breakpoint-md) {
        top: $header-md-height-total;
        bottom: 0;
        left: auto;
        width: 360px;
        height: calc(100% - #{$header-md-height-total});
        box-shadow: -10px 0 10px 0 rgba(0, 0, 0, .1);
        overflow-y: scroll;
        overflow-x: hidden;
    
        &--cart {
          overflow-y: hidden;
        }
    
        .js-promo-bar-open & {
          top: $header-md-height-total + $promo-bar-md-height;
          height: calc(100% - #{$header-md-height-total + $promo-bar-md-height});
        }
      }
    
      @include breakpoint($breakpoint-lg) {
        top: $header-lg-height-total;
        height: calc(100% - #{$header-lg-height-total});
    
        .js-promo-bar-open & {
          top: $header-lg-height-total + $promo-bar-lg-height;
          height: calc(100% - #{$header-lg-height-total + $promo-bar-lg-height});
        }
      }
    
      @if $brand == 'b2b' {
        @include breakpoint($breakpoint-xl) {
          top: $header-xl-height-total;
          height: calc(100% - #{$header-xl-height-total});
        }
      }
    
      .js-header-flyout-search-open &--search,
      .js-header-flyout-cart-open &--cart {
        display: block;
    
        @include breakpoint($breakpoint-sm-only) {
          &.header-flyout--with-fallback {
            display: none;
          }
        }
      }
    }
    
    .header-flyout__header {
      position: absolute;
      top: 0;
      right: 0;
      left: 0;
      height: 50px;
      padding: 10px;
      background-color: $color-lighter;
      box-sizing: border-box;
    }
    
    .header-flyout__close {
      @include reset-button;
      position: relative;
      width: 50px;
      height: 50px;
      float: right;
      z-index: 1;
    
      .icon {
        width: 16px;
        height: 16px;
        pointer-events: none;
      }
    
      .header-flyout--search & {
        margin: 10px 5px 10px 0;
    
        @include breakpoint($breakpoint-md) {
          margin-right: 10px;
        }
      }
    }
    
    // We do this instead of:
    //   .header-flyout__content {
    //     .header-flyout--cart & {
    //       styles here…
    // because that does not work with the way we scope styles for B2B.
    .header-flyout--cart {
      .header-flyout__content {
        position: absolute;
        top: 50px;
        right: 0;
        bottom: 0;
        left: 0;
      }
    }
    
    .header-flyout__toggle {
      @include reset-button;
    
      @include breakpoint($breakpoint-sm-only) {
        &--with-fallback {
          display: none;
        }
      }
    }
    
    @if $brand == 'b2b' {
      .header-flyout__toggle--cart,
      .header-flyout__link--cart {
        margin: 2rem;
    
        .icon {
          margin-top: .8rem;
        }
    
        .header-flyout__badge {
          position: absolute;
          top: 0;
          right: .5rem;
          left: 0;
          height: 1.2rem;
          color: $color-black;
          font-size: 1.2rem;
          text-align: center;
        }
      }
    }
    
    .header-flyout__link {
      // Note that we have to set the display value for B2B in _reset-b2b due to
      // the specificity with which their base styles set the display on links.
      display: inline-block;
    
      @include breakpoint($breakpoint-md) {
        display: none;
      }
    }
    
    // To-do: Implement this as a generic alt icon (i.e. not cart-specific).
    .header-flyout__toggle,
    .header-flyout__link {
      position: relative;
    
      @if $brand != 'b2b' {
        .icon--cart,
        .icon--cart-item {
          @include breakpoint($breakpoint-sm-only) {
            width: 25px;
            height: 22px;
          }
        }
    
        .icon--cart {
          display: none;
        }
    
        &.js-header-flyout-icon-empty-cart {
          .icon--cart-item {
            display: none;
          }
    
          .icon--cart {
            display: inline-block;
          }
        }
      }
    }
    
    .js-header-flyout-search-open,
    .js-header-flyout-cart-open {
      position: fixed;
      width: 100%;
      height: 100%;
      overflow-x: hidden;
      overflow-y: scroll;
    }
    
    // Darkened overlay covering the screen when flyouts are open.
    .header-flyout__overlay {
      display: none;
      position: fixed;
      top: $header-sm-height-total;
      right: 0;
      bottom: 0;
      left: 0;
      height: calc(100vh - #{$header-sm-height-total});
      background-color: $color-black;
      content: '';
      opacity: .5;
      z-index: 11;
    
      @include breakpoint($breakpoint-md) {
        top: $header-md-height-total;
        height: calc(100vh - #{$header-md-height-total});
    
        .js-promo-bar-open & {
          top: $header-md-height-total + $promo-bar-md-height;
          height: calc(100vh - #{$header-md-height-total + $promo-bar-md-height});
        }
      }
    
      @include breakpoint($breakpoint-lg) {
        top: $header-lg-height-total;
        height: calc(100vh - #{$header-lg-height-total});
    
        .js-promo-bar-open & {
          top: $header-lg-height-total + $promo-bar-lg-height;
          height: calc(100vh - #{$header-lg-height-total + $promo-bar-lg-height});
        }
      }
    
      @if $brand == 'b2b' {
        @include breakpoint($breakpoint-xl) {
          top: $header-xl-height-total;
          height: calc(100vh - #{$header-xl-height-total});
        }
      }
    
      // To-do: refactor the overlay so that all flyouts share a single one.
      .js-header-flyout-search-open &--search,
      .js-header-flyout-cart-open &--cart {
        display: block;
    
        // This is necessary on because of the scoping we do in b2b.scss.
        // To-do: Find a better way to accomplish this.
        @if $brand == 'b2b' {
          @include breakpoint($breakpoint-sm-only) {
            display: none;
          }
        }
      }
    
      // Don’t show overlay at small breakpoint if flyout has fallback.
      // To-do: Find a better way to accomplish this.
      .js-header-flyout-cart-open .header-flyout--with-fallback ~ & {
        @include breakpoint($breakpoint-sm-only) {
          display: none;
        }
      }
    }
    /* critical:end */
    
  • URL: /components/raw/header-flyout/header-flyout.scss
  • Filesystem Path: src/components/02-components/header-flyout/header-flyout.scss
  • Size: 5.9 KB

There are no notes for this item.