Copy paste element test tool

by Hugo Carneiro

HTML

<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script>
<div class="container">
  <div class="alert alert-primary" role="alert">
    Paste using CMD/CTRL + V
  </div>
  
  <hr>
</div>

<div id="container" class="container">
  <div class="element" data-id="1">
    <div class="placeholder-screen">
      <p>
        Copying
      </p>
    </div>
    <small>Element 1</small>
    <p>
      Click me and copy me with CMD/CTRL + C
    </p>
  </div>

  <div class="element" data-id="2">
    <div class="placeholder-screen">
      <p>
        Copying
      </p>
    </div>
    <small>Element 2</small>
    <p>
      Click me and copy me with CMD/CTRL + C
    </p>
  </div>
</div>

<hr>

<div id="settings" class="container">
  <h3>
    Tweak delay of pasting
  </h3>
  <p>
    This will simulate an API response time.
  </p>
  <div class="slidecontainer">
    <input type="range" min="0" max="10000" value="130" class="slider" id="timeDelayRange">
    <input type="text" value="130" class="fomr-control" id="timeDelay">
  </div>
</div>


<div class="toast toast-copy" role="alert" aria-live="assertive" aria-atomic="true" data-delay="2000" style="position: fixed; bottom: 15px; right: 50%; margin-right: -102px;">
  <div class="toast-header">
    <strong class="mr-auto">Copied</strong>
    <button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
      <span aria-hidden="true">&times;</span>
    </button>
  </div>
  <div class="toast-body">
    Your element was copied.
  </div>
</div>

SCSS

.element {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-flow: column wrap;
  border: 1px solid rgba(51, 51, 51, 0.2);
  padding: 15px;
  margin: 15px 0;
  cursor: pointer;

  p {
    margin: 0;
  }

  &.active {
    border: 1px solid rgba(#F3D5B1, 0.5);
    background: rgba(#F3D5B1, 0.4);
  }
  
  .placeholder-screen {
    display: none;
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 1;
    background-image: linear-gradient(45deg,rgba(#F3D5B1,1) 25%,transparent 25%,transparent 50%,rgba(#F3D5B1,1) 50%,rgba(#F3D5B1,1) 75%,transparent 75%,transparent);
    border: 2px solid #F3D5B1;
    background-size: 1rem 1rem;
    animation: progress-stripes 1s linear infinite;
 }
  
  &.placeholder {
    border: 1px solid rgba(#F3D5B1, 0.5);
    
    .placeholder-screen {
      display: flex;
      align-items: center;
      justify-content: center;
      
      p {
        margin: 0;
        background: #fff;
        padding: 6px 12px;
      }
    }
  }
}

hr {
  margin: 50px 0;  
}

.container {
  .alert {
    margin-top: 15px;
  }  

  hr {
    margin: 15px 0;
  }
}

#settings {
  .slider {
    -webkit-appearance: none;
    width: 100%;
    height: 25px;
    background: #d3d3d3;
    outline: none;
    opacity: 0.7;
    -webkit-transition: .2s;
    transition: opacity .2s;

    &:hover {
      opacity: 1;
    }

    &::-webkit-slider-thumb {
      -webkit-appearance: none;
      appearance: none;
      width: 25px;
      height: 25px;
      background: #4CAF50;
      cursor: pointer;
    }

    &::-moz-range-thumb {
      width: 25px;
      height: 25px;
      background: #4CAF50;
      cursor: pointer;
    }
  }
}

@keyframes progress-stripes {
  0%   { background-position: 1rem 0; }
  100% { background-position: 0 0; }
}

JavaScript

let selectedElementId;
let copiedElementId;
let timeDelay = 130;
let acceptableTimeWithoutUI = 300;

function insertAfterSelected($copiendElement) {
	$('#container').find(`[data-id="${selectedElementId}"]`).removeClass('active').after($copiendElement);

	// Fake endpoint response time
  setTimeout(function() {
  	$copiendElement.removeClass('placeholder').addClass('active');
    selectedElementId = $copiendElement.data('id');
  }, timeDelay);
}

function appendElement() {
  $('#container').append($copiendElement);

 	// Fake endpoint response time
  setTimeout(function() {
  	$copiendElement.removeClass('placeholder').addClass('active');
    selectedElementId = $copiendElement.data('id');
  }, timeDelay);
}

function onKeyDown(evt) {
  const key = evt.keyCode;
  const isControlOrCommand = evt.ctrlKey || evt.metaKey;

  // CTRL + C
  if (isControlOrCommand && key === 67) {
    if (!selectedElementId) {
      return;
    }

    $('.toast-copy').toast('show');

    copiedElementId = selectedElementId;
  }

  // CTRL + V
  if (isControlOrCommand && key === 86) {
    if (!copiedElementId) {
      return;
    }

		// Processing copied element
    const length = $('#container').find('.element').length;
    const $copiendElement = $('#container').find(`[data-id="${copiedElementId}"]`).clone();
    const elementText = $copiendElement.find('small').html();
    
    $copiendElement.attr('data-id', length + 1).addClass('active').find('small').html(`Copy of ${elementText}`);
    
    // Checks if loading UI needs to be added
    if (timeDelay > acceptableTimeWithoutUI) {
    	$copiendElement.removeClass('active').addClass('placeholder');
    }
    
    // Insert element
    if (selectedElementId) {
      insertAfterSelected($copiendElement);
    } else {
      appendElement($copiendElement);
    }
  }
}

$(document)
  .on('click', '.element', function() {
  	$('.element.active').not($(this)).removeClass('active');
    $(this).toggleClass('active');

    if...