Remove Toast With Panning (Using mouse & touch events)

by Konstantin Rouda

HTML

<main class="c-main">
  <div id="toastsContainer" class="c-toasts-container c-toasts-container--left c-toasts-container--from-bottom">
    <div class="c-toast c-toast--visible" role="alert" id="toast" aria-live="assertive" aria-atomic="true">
      <h4 class="c-toast__title">Toast Title</h4>
      <div class="c-toast__content">User has been successfully created</div>
    </div>
  </div>
</main>

CSS

HTML {
  /*using system font-stack*/
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
  font-size: calc(16px + (20 - 16) * (100vw - 300px) / (1100 - 300));
  line-height: 1.5;
  box-sizing: border-box;
}

BODY {
  height: 720px;
  margin: 0;
  padding-bottom: 2rem;
  color: rgb(58, 61, 64);
  background-color: rgb(254, 254, 254);
}

*,
*::before,
*::after {
  box-sizing: inherit;
  color: inherit;
}

/*Actual Style*/
.c-main {
  display: flex;
  height: 100vh;
}

.panning {
  user-select: none;
  transition: none !important;
  opacity: 1;
}

.panning * {
  pointer-events: none;
}

/**TOAST STYLE**/
/*===================================
  TOASTS CONTAINER
===================================*/
.c-toasts-container {
  position: fixed;
  top: 0;
  bottom: 0;
  min-width: 250px;
  width: 100%;
  max-width: 100%;
  min-height: 100px;
  pointer-events: none;
  box-sizing: border-box;
  z-index: 9999;
}

.c-toasts-container *,
.c-toasts-container *::before,
.c-toasts-container *::after {
  box-sizing: inherit;
}

@media screen and (min-width: 600px) {
  .c-toasts-container {
    max-width: 50%;
  }
  /* POSITIONS
    -------------------*/
  .c-toasts-container--left {
    left: 0.5em;
  }

  .c-toasts-container--right {
    right: 0.5em;
  }
}

/*===================================
    TOAST
  ===================================*/
.c-toast {
  opacity: 0;
  position: absolute;
  display: flex;
  flex-direction: column;
  max-width: 100%;
  min-height: 50px;
  margin-bottom: 1em;
  border-radius: 0.3em;
  background: rgba(42, 44, 46, 0.98);
  color: rgba(248, 248, 248, 1);
  line-height: 1.5;
  box-shadow: 0 2px 8px -2px rgba(0, 0, 0, 0.3);
  pointer-events: all;
  transition: transform 0.7s, opacity 0.6s 0.2s;
}

/*align toast to the right*/
.c-toasts-container--right .c-toast {
  right: 0;
}

/*--> FROM TOP <--*/
.c-toasts-container--from-top .c-toast {
  top: 0;
  transform:...

JavaScript

;(function () {
  "use strict";
  
  const toast = document.getElementById("toast");
  
  toast.addEventListener("mousedown", onDragStart);
  document.addEventListener("mousemove", onDrag);
  document.addEventListener("mouseup", onDragEnd);
  toast.addEventListener("mouseleave", onDragEnd);
  document.addEventListener("touchstart", onDragStart);
  document.addEventListener("touchmove", onDrag);
  document.addEventListener("touchend", onDragEnd);
  
  function onDragStart (e) {
    const toast = e.currentTarget;
    //debugger;
    toast.classList.add("panning");
    
    const translateYVal = getComputedStyle(toast).transform.match(/(-?\d\.?\d*)\)$/)[1];
    
    let intialTranslateY = null;
    
    if(translateYVal) {
    	intialTranslateY = Math.round((translateYVal / toast.offsetHeight) * 100);
    }
    
    if(intialTranslateY) {
    	toast.intialTranslateY = intialTranslateY;
    }
    
    toast.dragStartTime = performance.now();
    
    if(e.type === "touchstart") {
      toast.initX = e.touches[0].clientX;
    } else {
      toast.initX = e.clientX;
    }
  }
  
  function onDrag (e) {
    const toast = e.target;
    if(!toast.classList.contains("panning")) return;
    e.preventDefault();
    
    let clientX = e.clientX;
    
    if(e.type === "touchmove") {
      clientX = e.touches[0].clientX;
    }
    
    let x = clientX - toast.initX;
    
    toast.dragEndTime = performance.now();
    //debugger;
    translateX(toast, x);
  }
  
  function onDragEnd (e) {
    const toast = e.target;

    if(!toast.classList.contains("panning")) return;
    e.preventDefault();
    toast.classList.remove("panning");
    translateX(toast, 0);
  }
  
  // UTILITIES
  function translateX(elem, newPos) {
    let pos = 0;
    let opacity = 1;
    let moveTime = 0;

    if(newPos !== 0) {
       pos = newPos;/* - (elem.offsetWidth / 2);*/
   		// mouse speed (https://stackoverflow.com/questions/6417036/track-mouse-speed-with-js)
   		moveTime = Math.abs(Math.round(pos...