Created
July 13, 2019 09:12
-
-
Save toomanyredirects/4f6e27a970d391cc2b935f656c95a8e4 to your computer and use it in GitHub Desktop.
HTML5 Drag & Drop File Input
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<body> | |
<main id="content" role="main"> | |
<h1>HTML5 <code><input type="file"/></code></h1> | |
<p> | |
A <b>HTML5 file input</b> variations normalized with Javascript and SCSS as a <abbr title="Progressive enhancement is a strategy for web design that emphasizes core webpage content first. This strategy then progressively adds more nuanced and technically rigorous layers of presentation and features on top of the content as the end-user's browser/internet connection allow.">progressive enhancement</abbr> with cross-browser support. | |
</p> | |
<p> | |
<!-- Example code --> | |
<i class="loading ellipsis" data-loading="loading"></i> | |
<!-- Example code / --> | |
</p> | |
<section> | |
<!-- Upload file - Code example 1 --> | |
<form action="" method="post" enctype="multipart/form-data" > | |
<fieldset class="upload dropzone" data-dropzone="Drop file here..."> | |
<input id="file-upload" type="file" accept="image/*,.pdf" required /> | |
<label for="file-upload" data-label="Select a file:"></label> | |
<ul class="preview" data-empty="Drag or browse for a file."></ul> | |
<label for="file-upload" class="icon file" title="Browse"></label> | |
<description class="info"></description> | |
<em class="error" | |
data-nosupport="File API not supported." | |
data-nofile="No file selected." | |
data-required="Mandatory file upload." | |
data-count="Only one file allowed." | |
data-mime="Allowed files types: "></em> | |
</fieldset> | |
</form> | |
<!-- / Upload file - Code example 1 --> | |
<!-- Uplad multiple - Code example 2 --> | |
<form action="" method="post" enctype="multipart/form-data"> | |
<fieldset class="upload upload-multiple dropzone" data-dropzone="Drop files & folders here..."> | |
<input id="multi-upload" type="file" accept="*" multiple directory required /> | |
<label for="multi-upload" data-label="Select files & folders:"></label> | |
<ul class="preview" data-empty="Drag or browse for files & folders."></ul> | |
<i class="icon cloud" title="Upload files"></i> | |
<em class="error" | |
data-nosupport="File API not supported." | |
data-nofile="No file selected." | |
data-required="Mandatory file upload." | |
data-count="Only 100 simultaneous files allowed." | |
data-mime="Allowed files types: "></em> | |
</fieldset> | |
</form> | |
<!-- / Uplad multiple - Code example 2 --> | |
<!-- Upload photo booth - Code example 3 --> | |
<form action="" method="post" enctype="multipart/form-data"> | |
<fieldset class="upload upload-photo dropzone" data-dropzone="Drop image here..."> | |
<input id="image-upload" type="file" accept="image/*,capture=camera" capture="user" required /> | |
<label for="image-upload" data-label="Browse for files..."></label> | |
<ul class="preview circle" data-empty="Drag or browse for an image."></ul> | |
<i class="icon camera" title="Camera" data-video="Click to record." data-recording="Recording..."></i> | |
<description class="info"></description> | |
<em class="error" | |
data-nosupport="File API not supported." | |
data-nofile="No file selected." | |
data-required="Mandatory file upload." | |
data-count="Only one file allowed." | |
data-mime="Allowed files types: "></em> | |
</fieldset> | |
</form> | |
<!-- / Upload photo booth - Code example 3 --> | |
</section> | |
</main> | |
</body> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Drag & drop file input and input normalization */ | |
fileInput = function(elements){ | |
var selectors = elements || 'input[type="file"]', | |
imgRegx = /\.(jpe?g|png|svg|webp|bmp|gif)$/i; | |
document.querySelectorAll(selectors).forEach( function(input){ | |
var dropzone = input.form.querySelector('.dropzone') || document.body || document.documentElement.body, | |
parent = input.parentElement, | |
preview = parent.querySelector('.preview'), | |
isCapture = input.accept.match(/capture.*/g) || input.getAttribute('capture'); | |
/* Event listeners */ | |
dropzone.ondragover = function(e) { onDrag(e, dropzone); }; | |
dropzone.ondragleave = function(e) { onDrag(e, dropzone); }; | |
dropzone.ondrop = function(e) { onDrop(e, input, preview, dropzone); }; | |
input.onchange = function(e) { onDrop(e, input, preview, dropzone); }; | |
if(isCapture) | |
input.parentElement.querySelector('.camera').onclick = function(e) {capturing(e, preview, input);}; | |
} | |
); | |
/* Drag & drop functions */ | |
function onDrag (e, dropzone) { | |
// e = e || window.event;get window.event if e argument missing (in IE) | |
e.stopPropagation(); | |
e.preventDefault(); | |
if (e.type === 'dragover') dropzone.classList.add('dragover'); | |
else dropzone.classList.remove('dragover'); | |
} | |
/* Drop & error handling function */ | |
function onDrop (e, input, preview, dropzone) { | |
onDrag(e, dropzone); | |
if (!(window.File && window.FileReader && window.FileList && window.Blob)) {// Check File API support | |
return showError(input, 'nosupport', 'fileapi'); | |
} | |
var files = e.target.files || e.currentTarget.files || e.dataTransfer.files , | |
// folders = e.target.items || e.dataTransfer.items, | |
folders = (e.dataTransfer) ? e.dataTransfer.items : false, | |
accept = input.accept || '*', | |
acceptRegx = accept.split(',') || accept.split(';') || accept; | |
input.files = files; | |
if(folders && input.multiple){ | |
for(var i = 0; i < folders.length; i++) { | |
var dir = folders[i].webkitGetAsEntry(); | |
if (dir) directoryIterator(dir, null, preview); | |
} | |
return; // break further files processing | |
} | |
/* Preview handling */ | |
if (files.length === 1) { | |
if(!input.multiple){ | |
reset(preview); // Reset prior previews | |
if(input.parentElement.className.indexOf('upload-photo') > -1 && accept.match(/image.*/g)) | |
createPreview(files[0], preview, true, true); | |
else if (!files[0].type.match(accept)) | |
return showError(input, 'mime', accept); | |
else | |
createPreview(files[0], preview, true); | |
} else { | |
createPreview(files[0], preview); | |
} | |
} else if (files.length > 1) { | |
if (!input.multiple) { | |
return showError(input, 'count'); | |
} else { | |
for(var i = 0; i < files.length; i++) | |
createPreview(files[i], preview); | |
} | |
} else { | |
return showError(input, 'nofile'); | |
} | |
} | |
/* Error function */ | |
function showError(input, attribute, msg) { | |
var error = input.parentElement.querySelector('.error'), | |
data = input.parentElement.querySelector('[data-'+ attribute +']'), | |
msg = (data) ? data.getAttribute('data-'+ attribute) + (msg || '') | |
: 'Error'+ attribute +': '+ (msg || ''); | |
error.innerHTML = msg; | |
input.parentElement.classList.add('hasError'); | |
} | |
/* Create a preview item */ | |
function createPreview(file, preview, isSingle, isImage) { | |
var reader = new FileReader(), | |
img = new Image(), | |
item = document.createElement('li'), | |
btnRemove = document.createElement('i'); | |
btnRemove.onclick = function(e){ remove(btnRemove, preview); }; | |
reader.addEventListener("load", function () { | |
img.title = file.name; | |
img.alt = 'size: '+ file.size +', type: '+ file.type +', modified: '+ file.lastModified; | |
img.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'; | |
item.innerHTML = '<b>'+ file.name +'</b><small data-upload="0">'+ formatBytes(file.size) +' '+ file.type +'</small>'; | |
if (file.name.match(imgRegx)) { | |
img.style.backgroundImage = 'url('+ reader.result +')'; | |
if(isSingle && isImage) preview.style.backgroundImage = 'url('+ reader.result +')'; | |
} | |
item.insertAdjacentElement('beforeend', btnRemove); | |
item.insertAdjacentElement('afterbegin', img); | |
item.addEventListener('click', function(e){ upload(e, item, file); }, false); | |
preview.appendChild(item); | |
}, false); | |
reader.readAsDataURL(file); | |
} | |
/* Camera capture */ | |
function capturing(e, preview, input){ | |
navigator.mediaDevices.getUserMedia({ video:true }) | |
.then(function(stream) { | |
var video = document.createElement('video'), | |
control = preview.parentElement.querySelector('.camera'), | |
title = control.title || 'Camera', | |
recording = control.getAttribute('data-recording') || 'Recording...', | |
shutter = new Audio('https://www.soundjay.com/mechanical/camera-shutter-click-01.mp3'), | |
canvas = document.createElement('canvas'), | |
ctx = canvas.getContext('2d'), | |
w, h, ratio; | |
reset(preview); // Reset prior previews | |
preview.classList.remove('snaphot'); // Reset prior snapshots | |
preview.appendChild(video); | |
/* Recording button */ | |
control.classList.add('recording'); control.title = recording; | |
control.onclick = function(){ | |
reset(preview); this.title = title; | |
this.classList.remove('recording'); | |
}; | |
/* Camera input stream */ | |
video.srcObject = stream; | |
video.addEventListener('loadedmetadata', function() { | |
ratio = video.videoWidth/video.videoHeight; | |
w = video.videoWidth-100; | |
h = parseInt(w/ratio,10); | |
canvas.width = w; | |
canvas.height = h; | |
video.play(); | |
},false); | |
// video.style.zIndex = 1000; | |
control.addEventListener('click', function(e) { | |
e.preventDefault(); | |
// e.stopPropagation(); | |
ctx.fillRect(0, 0, w, h); | |
ctx.drawImage(video, 0, 0, w, h); | |
var blob = canvas.toDataURL('image/jpeg'), | |
now = new Date(), | |
created = now.toLocaleDateString('de-CH') +' at '+ now.toLocaleTimeString('de-CH'); | |
shutter.play(); | |
preview.classList.add('snaphot'); | |
reset(preview); control.title = title; | |
item = document.createElement('li'); | |
item.innerHTML = '<b>Snapshot</b><small data-upload="0">'+ created +'</small><i><i/>'; | |
preview.appendChild(item); | |
preview.style.backgroundImage = 'url('+ blob +')'; | |
}, false); | |
}) | |
.catch(function(err) { | |
showError(input, 'capture', err.name + ": " + err.message); | |
}); | |
} | |
/* Clear / empty the preview element and reset all controls */ | |
function reset(el) { | |
el.parentElement.classList.remove('hasError'); /* Reset possible errors */ | |
el.innerHTML = ''; el.style.backgroundImage = ''; | |
el.parentElement.querySelector('.icon').classList.remove('recording'); | |
fileInput(); | |
} | |
/* Remove fileElement from dom and fileList */ | |
function remove(el, preview) { | |
preview.removeChild(el.parentElement); | |
} | |
function upload(e, item, file) { | |
e.preventDefault(); | |
item.classList.add('uploading'); | |
item.addEventListener('click', function(){ | |
item.classList.remove('uploading'); | |
item.classList.add('uploaded'); | |
}, false); | |
/* | |
var data = new FormData(); | |
data.append('files', file); | |
var xhr = new XMLHttpRequest(); | |
xhr.open('POST', 'handler.cfm', true); | |
xhr.onload = function(e) { | |
if(this.status == 200) | |
console.log(e.currentTarget.responseText || e.target.responseText); | |
else | |
alert('Upload error'); | |
} | |
xhr.send(data);*/ | |
} | |
/* Format bytes to KB, MB */ | |
function formatBytes(number) { | |
if(number < 1024) return number + 'bytes'; | |
else if (number >= 1024 && number < 1048576) return (number/1024).toFixed(1) + 'KB'; | |
else if (number >= 1048576) return (number/1048576).toFixed(1) + 'MB'; | |
} | |
/* Directory traversing function */ | |
function directoryIterator(item, path, preview) { | |
path = path || ''; preview = preview || ''; | |
if (item.isFile) { // Get file | |
item.file(function(file) { | |
// console.log("File:", path + file.name); | |
createPreview(file, preview); | |
}); | |
} else if (item.isDirectory) { // Get folder contents | |
var dirReader = item.createReader(); | |
dirReader.readEntries(function(entries) { | |
for (var i=0; i<entries.length; i++) { | |
directoryIterator(entries[i], path + item.name + "/", preview); | |
} | |
}); | |
} | |
} | |
}; | |
// Initiate file inputs | |
// For special element selection: fileInput('.elements, elements, #element'); | |
fileInput(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@charset "utf-8"; | |
// Default SCSS layout variables for example | |
// ----------------------------------------- | |
// Google font import | |
@import url(https://fonts.googleapis.com/css?family=Raleway:200,400,500,800); | |
// Core colors | |
$primary-color: #5755d9 !default; | |
$primary-color-dark: darken($primary-color, 5%) !default; | |
$primary-color-light: lighten($primary-color, 4%) !default; | |
$secondary-color: lighten($primary-color, 37.5%) !default; | |
$secondary-color-dark: darken($secondary-color, 3%) !default; | |
$secondary-color-light: lighten($secondary-color, 3%) !default; | |
$highlight-color: #ffe9b3 !default; | |
// Dark / gray colors | |
$dark-color: #303742 !default; | |
$light-color: #fff !default; | |
$gray-color: lighten($dark-color, 55%) !default; | |
$gray-color-dark: darken($gray-color, 30%) !default; | |
$gray-color-light: lighten($gray-color, 20%) !default; | |
// Border radius | |
$border-radius: .2rem !default; | |
// Control colors | |
$success-color: #32b643 !default; | |
$warning-color: #ffb700 !default; | |
$error-color: #e85600 !default; | |
// Responsive breakpoints | |
$size-xxs: 380px !default; | |
$size-xs: 480px !default; | |
$size-sm: 600px !default; | |
$size-md: 840px !default; | |
$size-lg: 960px !default; | |
$size-xl: 1280px !default; | |
$size-2x: 1440px !default; | |
$responsive-breakpoint: $size-xs !default; | |
$responsive-breakpoint-md: $size-md !default; | |
// Z-index | |
$zindex-0: 1 !default; | |
$zindex-1: 100 !default; | |
$zindex-2: 200 !default; | |
$zindex-3: 300 !default; | |
$zindex-4: 400 !default; | |
// Layout | |
$layout-spacing: 1rem !default; | |
// base64 url encode hex color. Use: #{url-colour($primary-color)} | |
@function url-color($color) { | |
@return '%23' + str-slice('#{$color}', 2, -1) | |
} | |
// HTML5 file input frag & drop file / direcory upload | |
// Input File SCSS code | |
// ------------------------------------------ | |
// Note: No vendor prefixes added! | |
$upload-accent-color: $primary-color !default; | |
$upload-dropzone-bgcolor: rgba(0,0,0,.5) !default; | |
$upload-info-bgcolor: rgba(0,0,0,.75) !default; | |
$upload-info-fontcolor: $light-color !default; | |
$upload-icon-color: $light-color !default; | |
// Print Backgrounds in Chrome / Safari | |
@media print { | |
#container * { | |
-webkit-print-color-adjust: exact; | |
color-adjust: exact; | |
} | |
} | |
/* ------Basic reset-------- */ | |
*,*::after,*::before { | |
margin:0; | |
padding:0; | |
-webkit-box-sizing: border-box; | |
-moz-box-sizing: border-box; | |
box-sizing: border-box; | |
} | |
html * { | |
-webkit-font-smoothing: antialiased; | |
-moz-osx-font-smoothing: grayscale; | |
scroll-behavior: smooth; | |
} | |
// HTML5 file input upload | |
// with profile pic photo booth | |
// Animations | |
@keyframes bounce { | |
0% { transform: translateY(0); } | |
50% { transform: translateY(25%);} | |
100% { transform: translateY(0); } | |
} | |
@keyframes recording { | |
0% { transform: scale(1); background-color: currentColor; } | |
50% { transform: scale(.85); background-color: transparent;} | |
100% { transform: scale(1); background-color: currentColor; } | |
} | |
@keyframes flash { | |
0% { opacityr: 1; } | |
100% { opacityr: 0; } | |
} | |
// Disable selection document-wide | |
html { | |
user-select: none; // prevent selecting elements on drag | |
-webkit-user-select: none; | |
-moz-user-select: none; | |
-ms-user-select: none; | |
} | |
// HTML5 file input drag & drop file / direcory upload | |
// Loading ellipsis extends unitUX .loading module | |
.loading { | |
&.ellipsis { | |
width:auto; height: inherit; | |
display:inline-block; // needs to be set for non block elements | |
animation: none; | |
white-space: nowrap; | |
font-size: 1em; | |
font-style: normal; | |
cursor: progress; | |
&::first-letter { | |
text-transform: uppercase; | |
} | |
&::before{ // Check for available properties and insert | |
content: attr(data-loading); | |
content: 'loading'; // en-US as fallback (missing data-* property) | |
display:inline; // Nedds to be set inline, otherways make ::first-letter not to render | |
-webkit-animation: none; // Unset .loading module's prior animation | |
animation: none; | |
} | |
&::after { | |
content: '\2026'; // Ascii code for the ellipsis character | |
overflow: hidden; | |
display: inline-block; | |
vertical-align: bottom; | |
box-sizing:content-box; | |
width: 0; | |
text-align:left; | |
margin-right: 1.25em; | |
-webkit-animation: ellipsis steps(4, end) 2s infinite; | |
animation: ellipsis steps(4, end) 2s infinite; | |
} | |
} | |
} | |
@-webkit-keyframes ellipsis { | |
to { width: 1.25em; margin-right:0;} | |
} | |
@keyframes ellipsis { | |
to { width: 1.25em; margin-right:0;} | |
} | |
// Dropzone | |
.dropzone { | |
// Droppable hover | |
&.dragover { | |
pointer-events: all; | |
z-index: $zindex-4; // Highest index | |
position: relative; | |
color:$upload-info-fontcolor; | |
& * , & *:empty { | |
z-index:$zindex-0; | |
&::after, &::before { | |
pointer-events: none; | |
z-index:$zindex-0; | |
} | |
} | |
&::before, &::after { | |
content: ''; | |
z-index:$zindex-1; | |
pointer-events: none; | |
position: absolute; | |
display:inline; | |
} | |
&::before { | |
content: attr(data-dropzone); | |
text-shadow: 0 1px 1px rgba(0, 0, 0, .75); | |
left: 0; top: 0; bottom: 0; | |
width: 100%; height:100%; max-height:100%; | |
background-color: $upload-dropzone-bgcolor; | |
padding-top: 35%; | |
overflow:hidden; | |
} | |
&::after { | |
top: 35%; left: 50%; | |
margin: -2.5em; | |
width: 5em; height: 5em; | |
background: transparent no-repeat center center; | |
background-size: contain; | |
background-image: url('data:image/svg+xml;charset=utf-8,%3Csvg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" fill="#{url-color($upload-icon-color)}"%3E%3Cpath d="M51.7 26.7a20 20 0 0 0-19.7-16c-7.7 0-14.4 4.3-17.6 10.7A16 16 0 0 0 16 53.3h34.7C58.1 53.3 64 47.5 64 40c0-6.9-5.6-12.8-12.3-13.3zm-14.4 8v10.7H26.7V34.7h-8L32 21.3l13.3 13.3h-8z"/%3E%3C/svg%3E'); | |
animation: 1.5s bounce infinite; | |
} | |
label { | |
z-index:$zindex-3; | |
} | |
.preview { | |
pointer-events: none; | |
transform: scale(.7); | |
filter: blur(3px); | |
opacity:.2; | |
transition: all 250ms ease-in-out; | |
&:empty::after{ | |
content: ''; | |
} | |
} | |
} | |
} | |
// HTML5 form input file | |
// Upload class on parent fieldset / .input-group | |
.upload { | |
font-size: 1rem; // Sizing scale for 1em in the selector | |
color: $upload-info-fontcolor; | |
margin: 0 auto; padding: 0; | |
margin-top:2em; | |
position: relative; | |
width: 100%; | |
border: 0; | |
display: block; | |
// Hover state of icon (camer, cloud, folder) | |
&:hover { | |
& .icon { | |
transform: scale(.8); | |
&.cloud{ | |
transform: scale(.8) translate(-50%, 10%); | |
} | |
} | |
} | |
input[type="file"] { | |
position: absolute; | |
width: 0; height:0; | |
opacity: 0; | |
} | |
& label:not(.icon) { | |
position: absolute; display: block; | |
bottom: 0; margin: 0; padding: 0; | |
width: 100%; height:100%; | |
cursor: pointer; | |
pointer-events: all; | |
&::before { | |
z-index:$zindex-1; | |
} | |
&:after { | |
position: absolute; display: block; | |
content: attr(data-label); | |
left: 0; bottom: auto!important; top:-2em; | |
width: 100%; height: 1em; | |
padding: 0; | |
text-align:left; | |
text-shadow: 0 1px 1px $upload-info-bgcolor; | |
} | |
} | |
.icon { | |
position: absolute; display:block; overflow:visible; | |
bottom: .25em; right: .5em; | |
width:1em; height:1em; | |
pointer-events:all; | |
cursor:pointer; | |
transform: scale(1); | |
filter: drop-shadow(1px 1px 1px $upload-info-bgcolor); | |
transition: transform .25s ease; | |
font-size: 2em; font-style:normal; | |
&::before{ | |
content: url('data:image/svg+xml;charset=utf-8,%3Csvg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60" fill="#{url-color($upload-icon-color)}"%3E%3Cpath d="M14 24h-1L2 53v1l1 1h44l3-2 10-28v-1H14z"/%3E%3Cpath d="M13 22h41v-7c0-1-1-3-3-3H27l-5-6H3C1 6 0 7 0 8v42l10-27 3-1z"/%3E%3C/svg%3E'); | |
} | |
} | |
.preview { | |
margin:0; padding:0; | |
list-style:none; | |
text-align:left; | |
position:relative; | |
min-height:3em; | |
background: no-repeat center center; | |
background-size: contain; | |
-o-object-fit: contain; | |
object-fit: contain; | |
&:empty{ | |
pointer-events:none; | |
&::after{ | |
position:absolute; display:block; | |
content: attr(data-empty); | |
top:50%; left:0; width:100%; | |
transform:translateY(-50%); | |
} | |
} | |
li { | |
display:inline-block; box-sizing:border-box; | |
overflow:hidden; position: relative; | |
margin:0 2em 0 0; padding:.5em; | |
padding-right: 5.75em; | |
white-space: nowrap; | |
border-radius: $border-radius; | |
left:0; right:0; top:0; | |
width:100%; | |
min-height:3em; | |
line-height:1.1; | |
text-align: top; | |
vertical-align:top; | |
&.uploading { | |
small { | |
position:relative; | |
text-indent:100%; height: .5em; margin-top:.6em; // Total: 1.1em | |
background-color: rgba($upload-icon-color, .25); | |
background-image: linear-gradient(to right, $upload-accent-color 50%, transparent 50%); | |
&::after { | |
content: attr(data-upload); | |
position:absolute; display: block; | |
text-align: right; | |
right:0; top: -1.1em; | |
} | |
} | |
i:last-of-type::before{ | |
content: none; | |
} | |
} | |
&.uploaded { | |
} | |
img { | |
position: relative; display:block; | |
width:2em; height:2em; | |
margin:0 .75em 0 0; | |
float:left; | |
border: .25em solid white; | |
background: white no-repeat center center; | |
background-size: 100% auto; | |
background-size: contain; | |
-o-object-fit: contain; | |
object-fit: contain; | |
} | |
b, small { | |
display: block; overflow: hidden; | |
text-overflow: ellipsis; | |
} | |
b{ | |
font-weight: 600; | |
} | |
small{ | |
height:1.1em; // For upload progree bar in multiple | |
opacity:.7; | |
} | |
i{ | |
z-index:$zindex-1; | |
position:relative; display:inline-block; | |
float:right; top:-1.75em; right:-2.75em; | |
width: 2em; height: 0; | |
background-color:transparent; | |
&::before{ | |
position:absolute; display:block; | |
width: 1.7em; height: 1.7em; | |
margin:0; padding:.3em; | |
-o-object-fit: contain; | |
object-fit: contain; | |
border-radius: 50%; | |
cursor:pointer; pointer-events:all; | |
opacity:.75; | |
// transform: scale(.75); | |
} | |
&:last-of-type::before{ | |
content: url('data:image/svg+xml;charset=utf-8,%3Csvg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 124 124"%3E%3Cpath d="M16 105l89-89M16 16l89 89" stroke="#{url-color($upload-icon-color)}" stroke-width="15"/%3E%3C/svg%3E'); | |
} | |
&:hover, &:focus, &:active{ | |
&::before{ | |
background-color:$error-color; | |
opacity:1; | |
} | |
} | |
} | |
} | |
} | |
.error { | |
position:absolute; display:block; overflow: visible; | |
top:-2em; left:50%; | |
transform: translate(-50%, -1em); | |
background-color:$error-color; | |
border-radius: $border-radius; | |
padding: .5em .75em; | |
color: white; | |
font-style: normal; | |
text-align:center; | |
opacity:0; | |
visibility: hidden; | |
pointer-events: none; | |
filter: drop-shadow(0 .73em .5em rgba(0,0,0,.25)); | |
transition: opacity .25s ease-in-out; | |
&::after{ | |
content: ''; display: block; position: absolute; | |
width: .35; height: .35; | |
left:50%; bottom: -.7em; | |
border: .325em solid transparent; | |
border-width: .35em; | |
border-color: transparent transparent transparent $error-color; | |
transform: rotate(90deg) translateY(50%); | |
} | |
} | |
// Errors | |
&.hasError { | |
.error { | |
opacity:1; | |
visibility: visible; | |
} | |
} | |
// Multiple file & folder upload | |
&.upload-multiple{ | |
counter-reset: file-count; | |
label{ | |
display:block; | |
border: 1px dashed currentcolor; | |
border-radius: $border-radius; | |
} | |
.preview{ | |
display: block; | |
padding:0; margin: 5em .5em .5em .5em; | |
&:empty{ | |
text-align: center; | |
& ~ .cloud::after{ | |
// content: none; | |
opacity:0; | |
visibility:hidden; | |
} | |
} | |
li { | |
counter-increment: file-count; | |
background-color: transparent; | |
padding-right: 2.75em; | |
&.uploaded { | |
counter-increment: none; | |
} | |
} | |
} | |
.cloud{ | |
position:absolute; | |
margin:0; padding:0; | |
left:50%; top:.25em; | |
transform: scale(1) translateX(-50%); | |
height: 2em; width: 2em; | |
&::before{ | |
content: url('data:image/svg+xml;charset=utf-8,%3Csvg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" fill="#{url-color($upload-icon-color)}"%3E%3Cpath d="M51.7 26.7a20 20 0 0 0-19.7-16c-7.7 0-14.4 4.3-17.6 10.7A16 16 0 0 0 16 53.3h34.7C58.1 53.3 64 47.5 64 40c0-6.9-5.6-12.8-12.3-13.3zm-14.4 8v10.7H26.7V34.7h-8L32 21.3l13.3 13.3h-8z"/%3E%3C/svg%3E'); | |
} | |
&::after{ | |
top:-1.85em; left:-.45em; | |
content: counter(file-count); | |
display:block; position:relative; | |
width:1.5em; height:1.5em; | |
background-color: $warning-color; | |
opacity:1; visibility:visible; | |
border-radius:50%; | |
font-size: .6em; color: #FFF; | |
line-height:1.35; font-weight: 400; | |
text-align:center; | |
transition: all .5s ease-in-out; | |
animation: .5s bounce infinite; | |
} | |
} | |
} | |
// Photo booth | |
// Single image upload with camera capture | |
&.upload-photo{ | |
overflow: hidden; | |
width:100%; max-width: 250px; | |
// Hover / drag state of dropzone | |
&:hover { | |
& label:after{ | |
opacity: 1; | |
transform: translateY(0); | |
} | |
} | |
input[type="file"] { | |
&[accept*="capture"], &[capture]{ | |
& ~ .camera { | |
display:block; | |
} | |
} | |
} | |
label{ | |
z-index:$zindex-1; | |
position: absolute; display: block; | |
bottom: 0; margin:0; padding:0; | |
width: 100%; height:100%; | |
cursor: pointer; | |
pointer-events: all; | |
&:after { | |
position: absolute; display: block; | |
content: attr(data-label); | |
left: 0; bottom: 0!important; top: unset; | |
width: 100%; height: 3em; | |
padding: 0 1em; | |
background-color: $upload-dropzone-bgcolor; | |
color: #fff; | |
text-align:left; | |
line-height: 3em; | |
text-shadow: 0 1px 1px $upload-info-bgcolor; | |
opacity: 0; | |
transition: transform .25s ease, opacity .25s ease; | |
transform: translateY(100%); | |
} | |
} | |
.camera{ | |
z-index: $zindex-1; | |
&::before{ | |
content: url('data:image/svg+xml;charset=utf-8,%3Csvg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="#{url-color($upload-icon-color)}"%3E%3Cpath d="M50 40a15 15 0 1 0 0 30 15 15 0 0 0 0-30zm40-15H78a4.5 4.5 0 0 1-4-2.8l-3-9.4a4.5 4.5 0 0 0-4-2.8H33a4.5 4.5 0 0 0-4 2.8l-3 9.4a4.5 4.5 0 0 1-4 2.8H10A10 10 0 0 0 0 35v45a10 10 0 0 0 10 10h80a10 10 0 0 0 10-10V35a10 10 0 0 0-10-10zM50 80a25 25 0 1 1 0-50 25 25 0 0 1 0 50zm36.5-38a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7z"/%3E%3C/svg%3E'); | |
} | |
&.recording { // .recording class set by script | |
color: $error-color; | |
background-color: currentColor$error-color; | |
border-radius: 50%; | |
transform: scale(1); | |
animation: 1s recording infinite; | |
&::before{ | |
content: ''; | |
} | |
} | |
} | |
.preview { | |
padding:0; | |
text-align:center; | |
&.snapshot{ | |
&::after{ | |
z-index:1001; | |
content:''; display:inline-block; position:fixed; | |
background-color: #FFF; | |
left:0; top:0; bottom:0; | |
width:100%; | |
opacity: 1; | |
visibility: visible; | |
animation: 1s flash infinite; | |
} | |
} | |
&.portrait, &.landscape, &.square, &.circle { | |
overflow:hidden; | |
background-size: contain; | |
&::before { | |
content: ''; | |
display: block; | |
padding-top:100%; | |
} | |
} | |
&.square, &.circle { | |
background-position: center center; | |
} | |
&.circle { | |
border-radius:50%; | |
background-size: cover; | |
} | |
li{ | |
white-space: words; | |
position:absolute; | |
top:66%; left:50%; | |
padding-right: 2.75em; | |
max-width:70%; | |
transform:translate(-50%); | |
background-color: $upload-dropzone-bgcolor; | |
font-size:.8em; | |
text-shadow: 0 1px 1px $upload-info-bgcolor; | |
img{ | |
width:0; height:0; | |
display:none; | |
} | |
} | |
video { | |
pointer-events: all; | |
position:absolute; display:block; top:0; | |
height: 100%; width:auto; | |
pointer-events: all; | |
transform:translateX(-12.5%); | |
transition: all .25s linear; | |
&:hover, &:focus, &:active { | |
cursor:pointer; | |
} | |
} | |
} | |
.error { | |
bottom: 50%; top: auto; | |
} | |
} | |
} | |
// Responsiveness | |
// First break point upwards | |
@media screen and (min-width: $responsive-breakpoint) { | |
.upload { | |
&.upload-multiple { | |
.preview { | |
li{ | |
width:50%; | |
margin-right:0; | |
float:left; | |
b, strong, i { | |
font-size:.85em; | |
} | |
} | |
} | |
} | |
} | |
} | |
// Second break point upwards | |
@media screen and (min-width: $responsive-breakpoint-md) { | |
.upload { | |
&.upload-multiple { | |
.preview { | |
li{ | |
width:25%; | |
float:left; | |
text-align:center; | |
padding-right:.5em; | |
img{ | |
float:none; | |
width:100%; height:auto; | |
margin-bottom:$layout-spacing/2; | |
border-width:$layout-spacing; | |
} | |
i{ | |
position:absolute; | |
top:$layout-spacing; right:$layout-spacing; | |
&::before { | |
background-color: $dark-color; | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
// Presentation layout CSS | |
// ------------------------------------------ | |
html { | |
height:100%; | |
margin:0; padding:0; | |
// display:flex; | |
justify-content: center; | |
align-items: center; | |
} | |
body { | |
align-self: center; | |
background: $gray-color-dark; | |
background: linear-gradient(to right, $gray-color-dark, darken($gray-color-dark,23%)); | |
margin:$layout-spacing; padding: 0; | |
font-family:'Raleway', 'Helvetica', 'Arial', Arial, Helvetica, sans; | |
color:$gray-color-light; | |
font-size: 16px; | |
} | |
main {} | |
a { | |
color: $gray-color-light; | |
text-decoration: none; | |
&:link{ | |
color: $gray-color-light; | |
} | |
&:hover { | |
color: $gray-color; | |
} | |
&:focus, &:active { | |
color: $gray-color-dark; | |
} | |
} | |
h1, h2 { | |
font-weight:100; color:#FFF; | |
text-shadow: 1px 1px 0 rgba(0,0,0,.8); | |
display: block; width: 100%; | |
text-align:center; | |
} | |
h2 { | |
padding-bottom:0; | |
margin-bottom:0; | |
} | |
code {font-weight:600;} | |
p{ | |
margin: 2*$layout-spacing; | |
padding:0; | |
text-align:center; | |
abbr{ | |
cursor: help; | |
text-decoration:none; | |
text-decoration-line: dashed; | |
border-style: dotted; | |
border-width:0 0 1px 0; | |
} | |
} | |
section { | |
text-align:center; | |
margin: 0; | |
display: inline-block; width:100%; | |
} | |
form { | |
padding: 2em 1em; | |
border:0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment