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`;
...