Annotate

by mmarcon

HTML

<div class="annotable">
  <img src="https://cdn.dribbble.com/users/92629/screenshots/3630685/dashboard_mobile_small.png">
  <ul class="annotations">
  </ul>
</div>

SCSS

body {
  font-family: sans-serif;

  .annotable {
    position: relative;
    cursor: pointer;
    
    .annotations {
      li {
        list-style-type: none;
        margin: 0;
        padding: 0;
        position: absolute;
        background: #fff;
        display: block;
        padding: 24px;
        z-index: 3;
        margin-top: 12px;
        margin-left: 12px;
        display: block;
        border-radius: 2px;
        box-shadow: 0 2px 2px 0 rgba(0,0,0,0.14), 0 1px 5px 0 rgba(0,0,0,0.12), 0 3px 1px -2px rgba(0,0,0,0.2);
        
        input, label {
          display: block;
        }
        
        label, h3 {
          font-size: 14px;
          color: #34495e;
          font-weight: normal;
        }
        
        input {
          width: 200px;
          margin: 3px 0;
          background-color: transparent;
          border: none;
          border-bottom: 1px solid #9e9e9e;
          border-radius: 0;
          outline: none;
          height: 1.5rem;
          font-size: 1rem;
          margin: 0 0 20px 0;
          padding: 0;
          box-shadow: none;
          box-sizing: content-box;
          
          &::placeholder {
            color: #bdc3c7;
          }
        }
        
        a {
          color: #ffab40;
          text-transform: uppercase;
          text-decoration: none;
          font-size: 14px;
          display: inline-block;
          float: right;
          margin: 6px 0 6px 15px;
        }
        
        &.hidden {
          display: none;
        }
      }
    }

    .hotspot {
      width: 24px;
      height: 24px;
      border-radius: 50%;
      background-color: #2c3e50;
      position: absolute;
      z-index: 2;
      margin-top: -12px;
      margin-left: -12px;
      color: #fff;
      text-align: center;
      line-height: 24px;
      font-size: 12px;
      &:hover {
        border: 4px solid #ffab40;
        margin-top: -16px;
        margin-left: -16px;
      }
    }
  }

}

JavaScript

const d = document;
let c = 0;

const editTemplate = (id) => `
<form action="#">
<label for="evName">Event name</label>
<input type="text" name="evName" id="evName"/>
<h3>Parameters</h3>
<input type="text" name="param1Name" id="param1Name" placeholder="PARAMETER_NAME"/>
<input type="text" name="param1Value" id="param1Value" placeholder="parameter_value"/>
<a href="#" class="save" data-id="${id}">Save</a>
<a href="#" class="delete" data-id="${id}">Delete</a>
</form>
`;

d.querySelector('.annotable').addEventListener('click', e => {
  const target = e.target;
  switch(!0){
   case target.classList.contains('hotspot'): {
   	const targetAnnotation = e.currentTarget.querySelector(`#${target.dataset.id}`);
    const allAnnotations = e.currentTarget.querySelectorAll('.annotations li');
    [].forEach.call(allAnnotations, a => (a !== targetAnnotation) && a.classList.add('hidden'));
    targetAnnotation.classList.toggle('hidden');
    return;
   }
   case target.tagName === 'IMG': {
    return annotate(e);
   }
   case target.classList.contains('save'): {
   	const targetAnnotation = e.currentTarget.querySelector(`#${target.dataset.id}`);
    targetAnnotation.classList.add('hidden');
   	return;
   }
   case target.classList.contains('delete'): {
   	const targetAnnotation = e.currentTarget.querySelector(`#${target.dataset.id}`);
    const targeHotspot = e.currentTarget.querySelector(`.hotspot-${target.dataset.id}`);
    targetAnnotation.parentNode.removeChild(targetAnnotation);
    targeHotspot.parentNode.removeChild(targeHotspot);
   	return;
   }
   default:
   	return;
  }
});

function annotate(e) {
	const annotations = e.currentTarget.querySelector('.annotations');
  const name = c;
  const id = `annotation-${c}`;

  const hotspot = d.createElement('div');
  const annotation = d.createElement('li');

  annotation.setAttribute('id', id);
  annotation.innerHTML = editTemplate(id);
  annotation.classList.add('hidden');
  annotation.style.top = `${e.offsetY}px`;
 ...