Card wall
A very basic kanban-style card wall using HTML5's drag and drop.
Drag cards between columns, or click to view.
Based on the tutorial at http://www.html5rocks.com/en/tutorials/dnd/basics/
A very basic kanban-style card wall using HTML5's drag and drop.
Drag cards between columns, or click to view.
Based on the tutorial at http://www.html5rocks.com/en/tutorials/dnd/basics/
<div id="board"> | |
<div class="column"> | |
<ul class="card-list"> | |
<li class="card" draggable="true">Happy</li> | |
<li class="card" draggable="true">Grumpy</li> | |
<li class="card" draggable="true">Sleepy</li> | |
<li class="card" draggable="true">Doc</li> | |
<li class="card" draggable="true">Sneezy</li> | |
<li class="card" draggable="true">Dopey</li> | |
</ul> | |
</div> | |
<div class="column"> | |
<ul class="card-list"> | |
<li class="card" draggable="true">Bashful</li> | |
</ul> | |
</div> | |
<div class="column"> | |
<ul class="card-list"> | |
</ul> | |
</div> | |
<div id="modalOverlay"></div> | |
</div> |
var movingCard = null; | |
function startDragging(e) { | |
this.classList.add('dragging'); | |
movingCard = this; | |
e.dataTransfer.effectAllowed = 'move'; | |
e.dataTransfer.setData('text/html', this.innerHTML); | |
} | |
function dragInto(e) { | |
if (movingCard.parentNode.parentNode !== this){ | |
this.classList.add('over'); | |
} | |
} | |
function dragWithin(e) { | |
if (e.preventDefault) { | |
e.preventDefault(); | |
} | |
e.dataTransfer.dropEffect = 'move'; | |
return false; | |
} | |
function dragOut(e) { | |
this.classList.remove('over'); | |
} | |
function drop(e) { | |
if (e.stopPropagation) { | |
e.stopPropagation(); | |
} | |
if (movingCard.parentNode.parentNode !== this) { | |
this.getElementsByClassName('card-list')[0].appendChild(movingCard); | |
} | |
return false; | |
} | |
function stopDragging(e) { | |
[].forEach.call(columns, function (column) { | |
column.classList.remove('over'); | |
}); | |
movingCard.classList.remove('dragging'); | |
movingCard = null; | |
} | |
function clickCard(e) { | |
var cardLoc = this.getBoundingClientRect(); | |
var newCard = document.createElement('div'); | |
newCard.setAttribute('id', 'cardDetail'); | |
newCard.style.left = cardLoc.left + 'px'; | |
newCard.style.top = cardLoc.top + 'px'; | |
newCard.style.width = cardLoc.width + 'px'; | |
newCard.style.height = cardLoc.height + 'px'; | |
var frontSide = document.createElement('div'); | |
frontSide.classList.add('front'); | |
var contentWrapper = document.createElement('div'); | |
contentWrapper.classList.add('content'); | |
contentWrapper.innerHTML = this.innerHTML; | |
frontSide.appendChild(contentWrapper); | |
var flipFront = document.createElement('a'); | |
flipFront.setAttribute('href', '#'); | |
flipFront.innerHTML = 'flip'; | |
flipFront.addEventListener('click', flipCard, false); | |
flipFront.classList.add('flip-trigger'); | |
frontSide.appendChild(flipFront); | |
newCard.appendChild(frontSide); | |
var backSide = document.createElement('div'); | |
backSide.classList.add('back'); | |
var flipBack = document.createElement('a'); | |
flipBack.setAttribute('href', '#'); | |
flipBack.innerHTML = 'flip'; | |
flipBack.addEventListener('click', flipCard, false); | |
flipBack.classList.add('flip-trigger'); | |
backSide.appendChild(flipBack); | |
newCard.appendChild(backSide); | |
document.getElementById('board').appendChild(newCard); | |
setTimeout(function(){ expandCard(newCard); }, 50); | |
} | |
function expandCard(newCard) { | |
document.getElementById('modalOverlay').style.display = 'block'; | |
var cardWidth = window.innerWidth * 0.6; | |
if (cardWidth > 1000) { | |
cardWidth = 1000; | |
} | |
var cardHeight = cardWidth * 0.7; | |
if (cardHeight > window.innerHeight) { | |
cardHeight = window.innerHeight * 0.9; | |
cardWidth = cardHeight / 0.7; | |
} | |
newCard.style.width = cardWidth + 'px'; | |
newCard.style.height = cardHeight + 'px'; | |
newCard.style.left = (window.innerWidth - cardWidth) / 2 + 'px'; | |
newCard.style.top = (window.innerHeight - cardHeight) / 2 + 'px'; | |
} | |
function clickOverlay(e) { | |
closeDetail(); | |
} | |
function handleKeyup(e) { | |
if (e.keyCode === 27) { | |
var cardDetail = document.getElementById('cardDetail'); | |
if (cardDetail !== null && typeof cardDetail !== 'undefined') { | |
closeDetail(); | |
} | |
} | |
} | |
function closeDetail(){ | |
document.getElementById('modalOverlay').style.display = 'none'; | |
var cardDetail = document.getElementById('cardDetail'); | |
cardDetail.parentNode.removeChild(cardDetail); | |
} | |
function flipCard(e) { | |
e.preventDefault(); | |
document.getElementById('cardDetail').classList.toggle('flipped'); | |
return false; | |
} | |
var cards = document.querySelectorAll('#board .card'); | |
[].forEach.call(cards, function(card) { | |
card.addEventListener('dragstart', startDragging, false); | |
card.addEventListener('dragend', stopDragging, false); | |
card.addEventListener('click', clickCard, false); | |
}); | |
var columns = document.querySelectorAll('#board .column'); | |
[].forEach.call(columns, function(column) { | |
column.addEventListener('dragenter', dragInto, false); | |
column.addEventListener('dragover', dragWithin, false); | |
column.addEventListener('dragleave', dragOut, false); | |
column.addEventListener('drop', drop, false); | |
}); | |
var modalOverlay = document.getElementById('modalOverlay'); | |
modalOverlay.addEventListener('click', clickOverlay, false); | |
document.addEventListener('keyup', handleKeyup, false); |
@-webkit-keyframes wiggle { | |
0% { -webkit-transform: rotate(0deg); } | |
25% { -webkit-transform: rotate(3deg); } | |
75% { -webkit-transform: rotate(-3deg); } | |
100% { -webkit-transform: rotate(0deg); } | |
} | |
@keyframes wiggle { | |
0% { transform: rotate(0deg); } | |
25% { transform: rotate(3deg); } | |
75% { transform: rotate(-3deg); } | |
100% { transform: rotate(0deg); } | |
} | |
body, html { | |
height: 100%; | |
padding: 0; | |
margin: 0; | |
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; | |
font-size: 1em; | |
line-height: 1.5em; | |
color: #333; | |
background-color: #fff | |
} | |
#board { | |
height: 100%; | |
background-color: #dfdfdf; | |
overflow: hidden; | |
} | |
#board .column { | |
width: 33%; | |
float: left; | |
position: relative; | |
height: 100%; | |
border: solid 1px transparent; | |
border-right-color: #999; | |
-webkit-transition: background-color 300ms ease; | |
-moz-transition: background-color 300ms ease; | |
-ms-transition: background-color 300ms ease; | |
-o-transition: background-color 300ms ease; | |
transition: background-color 300ms ease; | |
} | |
#board .column:last-child { | |
border-right-color: transparent; | |
} | |
#board .column.over, #board .column.over:last-child { | |
border: dashed 1px #555; | |
background-color: #efefef; | |
} | |
#board ul.card-list { | |
list-style-type: none; | |
margin: 0; | |
padding: 2em; | |
} | |
#board ul.card-list .card { | |
float: left; | |
padding: 1em; | |
margin: 0 1em 1em 0; | |
border-radius: 8px; | |
border: solid 1px #bebebe; | |
background-color: #fff; | |
box-shadow: 1px 1px 1px rgba(0,0,0,0.3); | |
-webkit-transition: opacity 300ms ease; | |
-moz-transition: opacity 300ms ease; | |
-ms-transition: opacity 300ms ease; | |
-o-transition: opacity 300ms ease; | |
transition: opacity 300ms ease; | |
} | |
#board ul.card-list .card:hover { | |
-webkit-animation: wiggle 400ms; | |
animation: wiggle 400ms; | |
} | |
#board ul.card-list .card.dragging { | |
opacity: 0.5; | |
} | |
#board #cardDetail { | |
position: fixed; | |
z-index: 10000; | |
-webkit-transition: all 600ms ease-in; | |
-moz-transition: all 600ms ease-in; | |
-ms-transition: all 600ms ease-in; | |
-o-transition: all 600ms ease-in; | |
transition: all 600ms ease-in; | |
} | |
#board #cardDetail .front, | |
#board #cardDetail .back { | |
position: absolute; | |
width: 100%; | |
height: 100%; | |
border-radius: 8px; | |
text-align: center; | |
-webkit-backface-visibility: hidden; | |
-moz-backface-visibility: hidden; | |
-ms-backface-visibility: hidden; | |
-o-backface-visibility: hidden; | |
backface-visibility: hidden; | |
-webkit-transition: all 600ms ease-in; | |
-moz-transition: all 600ms ease-in; | |
-ms-transition: all 600ms ease-in; | |
-o-transition: all 600ms ease-in; | |
transition: all 600ms ease-in; | |
} | |
#board #cardDetail .front { | |
background-color: #fff; | |
border: solid 1px #bebebe; | |
z-index: 10000; | |
-webkit-transform: rotateY(0deg); | |
-moz-transform: rotateY(0deg); | |
-ms-transform: rotateY(0deg); | |
-o-transform: rotateY(0deg); | |
transform: rotateY(0deg); | |
} | |
#board #cardDetail .back { | |
background-color: #68C0E8; | |
border: solid 1px #0082BE; | |
-webkit-transform: rotateY(-179deg); | |
-moz-transform: rotateY(-179deg); | |
-ms-transform: rotateY(-179deg); | |
-o-transform: rotateY(-179deg); | |
transform: rotateY(-179deg); | |
} | |
#board #cardDetail.flipped .front { | |
-webkit-transform: perspective(1000px) rotateY(180deg); | |
-moz-transform: perspective(1000px) rotateY(180deg); | |
-ms-transform: perspective(1000px) rotateY(180deg); | |
-o-transform: perspective(1000px) rotateY(180deg); | |
transform: perspective(1000px) rotateY(180deg); | |
} | |
#board #cardDetail.flipped .back { | |
z-index: 20000; | |
-webkit-transform: perspective(1000px) rotateY(0deg); | |
-moz-transform: perspective(1000px) rotateY(0deg); | |
-ms-transform: perspective(1000px) rotateY(0deg); | |
-o-transform: perspective(1000px) rotateY(0deg); | |
transform: perspective(1000px) rotateY(0deg); | |
} | |
#board #cardDetail .content { | |
padding: 1em; | |
} | |
#board #cardDetail .flip-trigger { | |
position: absolute; | |
right: 10px; | |
bottom: 10px; | |
color: #333; | |
} | |
#board #cardDetail .flip-trigger:active, | |
#board #cardDetail .flip-trigger:hover, | |
#board #cardDetail .flip-trigger:focus { | |
color: #0082BE; | |
} | |
#modalOverlay { | |
display: none; | |
background-color: #444; | |
background-color: rgba(0,0,0,0.5); | |
position: absolute; | |
top:0; | |
left:0; | |
right:0; | |
bottom:0; | |
z-index:1000; | |
} | |
[draggable] { | |
-moz-user-select: none; | |
-khtml-user-select: none; | |
-webkit-user-select: none; | |
user-select: none; | |
-khtml-user-drag: element; | |
-webkit-user-drag: element; | |
} |