Improved error messages for image uploads and formatted much js

This commit is contained in:
Dan Brown 2015-12-30 19:57:17 +00:00
parent 46c2e8b14e
commit cca3533d35
5 changed files with 324 additions and 239 deletions

View File

@ -1,133 +1,133 @@
"use strict";
module.exports = function(ngApp) {
module.exports = function (ngApp) {
ngApp.controller('ImageManagerController', ['$scope', '$attrs', '$http', '$timeout','imageManagerService',
function($scope, $attrs, $http, $timeout, imageManagerService) {
$scope.images = [];
$scope.imageType = $attrs.imageType;
$scope.selectedImage = false;
$scope.dependantPages = false;
$scope.showing = false;
$scope.hasMore = false;
$scope.imageUpdateSuccess = false;
$scope.imageDeleteSuccess = false;
var page = 0;
var previousClickTime = 0;
var dataLoaded = false;
var callback = false;
$scope.getUploadUrl = function() {
return '/images/' + $scope.imageType + '/upload';
};
$scope.uploadSuccess = function(file, data) {
$scope.$apply(() => {
$scope.images.unshift(data);
});
};
function callbackAndHide(returnData) {
if (callback) callback(returnData);
ngApp.controller('ImageManagerController', ['$scope', '$attrs', '$http', '$timeout', 'imageManagerService',
function ($scope, $attrs, $http, $timeout, imageManagerService) {
$scope.images = [];
$scope.imageType = $attrs.imageType;
$scope.selectedImage = false;
$scope.dependantPages = false;
$scope.showing = false;
}
$scope.hasMore = false;
$scope.imageUpdateSuccess = false;
$scope.imageDeleteSuccess = false;
var page = 0;
var previousClickTime = 0;
var dataLoaded = false;
var callback = false;
$scope.imageSelect = function (image) {
var dblClickTime = 300;
var currentTime = Date.now();
var timeDiff = currentTime - previousClickTime;
$scope.getUploadUrl = function () {
return '/images/' + $scope.imageType + '/upload';
};
if (timeDiff < dblClickTime) {
// If double click
callbackAndHide(image);
} else {
// If single
$scope.selectedImage = image;
$scope.dependantPages = false;
}
previousClickTime = currentTime;
};
$scope.selectButtonClick = function() {
callbackAndHide($scope.selectedImage);
};
function show(doneCallback) {
callback = doneCallback;
$scope.showing = true;
// Get initial images if they have not yet been loaded in.
if (!dataLoaded) {
fetchData();
dataLoaded = true;
}
}
imageManagerService.show = show;
imageManagerService.showExternal = function(doneCallback) {
$scope.$apply(() => {
show(doneCallback);
});
};
window.ImageManager = imageManagerService;
$scope.hide = function() {
$scope.showing = false;
};
function fetchData() {
var url = '/images/' + $scope.imageType + '/all/' + page;
$http.get(url).then((response) => {
$scope.images = $scope.images.concat(response.data.images);
$scope.hasMore = response.data.hasMore;
page++;
});
}
$scope.saveImageDetails = function(event) {
event.preventDefault();
var url = '/images/update/' + $scope.selectedImage.id;
$http.put(url, this.selectedImage).then((response) => {
$scope.imageUpdateSuccess = true;
$timeout(() => {
$scope.imageUpdateSuccess = false;
}, 3000);
}, (response) => {
var errors = response.data;
var message = '';
Object.keys(errors).forEach((key) => {
message += errors[key].join('\n');
$scope.uploadSuccess = function (file, data) {
$scope.$apply(() => {
$scope.images.unshift(data);
});
$scope.imageUpdateFailure = message;
$timeout(() => {
$scope.imageUpdateFailure = false;
}, 5000);
});
};
};
$scope.deleteImage = function(event) {
event.preventDefault();
var force = $scope.dependantPages !== false;
var url = '/images/' + $scope.selectedImage.id;
if (force) url += '?force=true';
$http.delete(url).then((response) => {
$scope.images.splice($scope.images.indexOf($scope.selectedImage), 1);
$scope.selectedImage = false;
$scope.imageDeleteSuccess = true;
$timeout(() => {
$scope.imageDeleteSuccess = false;
}, 3000);
}, (response) => {
// Pages failure
if (response.status === 400) {
$scope.dependantPages = response.data;
function callbackAndHide(returnData) {
if (callback) callback(returnData);
$scope.showing = false;
}
$scope.imageSelect = function (image) {
var dblClickTime = 300;
var currentTime = Date.now();
var timeDiff = currentTime - previousClickTime;
if (timeDiff < dblClickTime) {
// If double click
callbackAndHide(image);
} else {
// If single
$scope.selectedImage = image;
$scope.dependantPages = false;
}
});
};
previousClickTime = currentTime;
};
}]);
$scope.selectButtonClick = function () {
callbackAndHide($scope.selectedImage);
};
function show(doneCallback) {
callback = doneCallback;
$scope.showing = true;
// Get initial images if they have not yet been loaded in.
if (!dataLoaded) {
fetchData();
dataLoaded = true;
}
}
imageManagerService.show = show;
imageManagerService.showExternal = function (doneCallback) {
$scope.$apply(() => {
show(doneCallback);
});
};
window.ImageManager = imageManagerService;
$scope.hide = function () {
$scope.showing = false;
};
function fetchData() {
var url = '/images/' + $scope.imageType + '/all/' + page;
$http.get(url).then((response) => {
$scope.images = $scope.images.concat(response.data.images);
$scope.hasMore = response.data.hasMore;
page++;
});
}
$scope.saveImageDetails = function (event) {
event.preventDefault();
var url = '/images/update/' + $scope.selectedImage.id;
$http.put(url, this.selectedImage).then((response) => {
$scope.imageUpdateSuccess = true;
$timeout(() => {
$scope.imageUpdateSuccess = false;
}, 3000);
}, (response) => {
var errors = response.data;
var message = '';
Object.keys(errors).forEach((key) => {
message += errors[key].join('\n');
});
$scope.imageUpdateFailure = message;
$timeout(() => {
$scope.imageUpdateFailure = false;
}, 5000);
});
};
$scope.deleteImage = function (event) {
event.preventDefault();
var force = $scope.dependantPages !== false;
var url = '/images/' + $scope.selectedImage.id;
if (force) url += '?force=true';
$http.delete(url).then((response) => {
$scope.images.splice($scope.images.indexOf($scope.selectedImage), 1);
$scope.selectedImage = false;
$scope.imageDeleteSuccess = true;
$timeout(() => {
$scope.imageDeleteSuccess = false;
}, 3000);
}, (response) => {
// Pages failure
if (response.status === 400) {
$scope.dependantPages = response.data;
}
});
};
}]);
ngApp.controller('BookShowController', ['$scope', '$http', '$attrs', function($scope, $http, $attrs) {
ngApp.controller('BookShowController', ['$scope', '$http', '$attrs', function ($scope, $http, $attrs) {
$scope.searching = false;
$scope.searchTerm = '';
$scope.searchResults = '';
@ -151,7 +151,7 @@ module.exports = function(ngApp) {
}
};
$scope.clearSearch = function() {
$scope.clearSearch = function () {
$scope.searching = false;
$scope.searchTerm = '';
};

View File

@ -5,25 +5,25 @@ var toggleSwitchTemplate = require('./components/toggle-switch.html');
var imagePickerTemplate = require('./components/image-picker.html');
var dropZoneTemplate = require('./components/drop-zone.html');
module.exports = function(ngApp) {
module.exports = function (ngApp) {
/**
* Toggle Switches
* Has basic on/off functionality.
* Use string values of 'true' & 'false' to dictate the current state.
*/
ngApp.directive('toggleSwitch', function() {
ngApp.directive('toggleSwitch', function () {
return {
restrict: 'E',
template: toggleSwitchTemplate,
scope: true,
link: function(scope, element, attrs) {
link: function (scope, element, attrs) {
scope.name = attrs.name;
scope.value = attrs.value;
scope.isActive = scope.value == true && scope.value != 'false';
scope.value = (scope.value == true && scope.value != 'false') ? 'true' : 'false';
scope.switch = function() {
scope.switch = function () {
scope.isActive = !scope.isActive;
scope.value = scope.isActive ? 'true' : 'false';
}
@ -37,7 +37,7 @@ module.exports = function(ngApp) {
* Image Picker
* Is a simple front-end interface that connects to an ImageManager if present.
*/
ngApp.directive('imagePicker', ['$http', 'imageManagerService', function($http, imageManagerService) {
ngApp.directive('imagePicker', ['$http', 'imageManagerService', function ($http, imageManagerService) {
return {
restrict: 'E',
template: imagePickerTemplate,
@ -52,7 +52,7 @@ module.exports = function(ngApp) {
defaultImage: '@',
imageClass: '@'
},
link: function(scope, element, attrs) {
link: function (scope, element, attrs) {
var usingIds = typeof scope.currentId !== 'undefined' || scope.currentId === 'false';
scope.image = scope.currentImage;
scope.value = scope.currentImage || '';
@ -62,22 +62,22 @@ module.exports = function(ngApp) {
scope.value = usingIds ? imageModel.id : imageUrl;
}
scope.reset = function() {
scope.reset = function () {
setImage({id: 0}, scope.defaultImage);
};
scope.remove = function() {
scope.remove = function () {
scope.image = 'none';
scope.value = 'none';
};
scope.showImageManager = function() {
scope.showImageManager = function () {
imageManagerService.show((image) => {
scope.updateImageFromModel(image);
});
};
scope.updateImageFromModel = function(model) {
scope.updateImageFromModel = function (model) {
var isResized = scope.resizeWidth && scope.resizeHeight;
if (!isResized) {
@ -102,7 +102,7 @@ module.exports = function(ngApp) {
* DropZone
* Used for uploading images
*/
ngApp.directive('dropZone', [function() {
ngApp.directive('dropZone', [function () {
return {
restrict: 'E',
template: dropZoneTemplate,
@ -111,26 +111,32 @@ module.exports = function(ngApp) {
eventSuccess: '=',
eventError: '='
},
link: function(scope, element, attrs) {
link: function (scope, element, attrs) {
var dropZone = new DropZone(element[0].querySelector('.dropzone-container'), {
url: scope.uploadUrl,
init: function() {
init: function () {
var dz = this;
dz.on('sending', function(file, xhr, data) {
dz.on('sending', function (file, xhr, data) {
var token = window.document.querySelector('meta[name=token]').getAttribute('content');
data.append('_token', token);
});
if (typeof scope.eventSuccess !== 'undefined') dz.on('success', scope.eventSuccess);
dz.on('success', function(file, data) {
dz.on('success', function (file, data) {
$(file.previewElement).fadeOut(400, function () {
dz.removeFile(file);
});
});
if (typeof scope.eventError !== 'undefined') dz.on('error', scope.eventError);
dz.on('error', function (file, errorMessage, xhr) {
if (errorMessage.file) {
$(file.previewElement).find('[data-dz-errormessage]').text(errorMessage.file[0]);
console.log(errorMessage);
console.log(xhr);
function setMessage(message) {
$(file.previewElement).find('[data-dz-errormessage]').text(message);
}
if (xhr.status === 413) setMessage('The server does not allow uploads of this size. Please try a smaller file.');
if (errorMessage.file) setMessage(errorMessage.file[0]);
});
}
});
@ -139,14 +145,14 @@ module.exports = function(ngApp) {
}]);
ngApp.directive('dropdown', [function() {
ngApp.directive('dropdown', [function () {
return {
restrict: 'A',
link: function(scope, element, attrs) {
link: function (scope, element, attrs) {
var menu = element.find('ul');
element.find('[dropdown-toggle]').on('click', function() {
element.find('[dropdown-toggle]').on('click', function () {
menu.show().addClass('anim menuIn');
element.mouseleave(function() {
element.mouseleave(function () {
menu.hide();
menu.removeClass('anim menuIn');
});

View File

@ -18,8 +18,8 @@ var controllers = require('./controllers')(ngApp);
//Global jQuery Config & Extensions
// Smooth scrolling
jQuery.fn.smoothScrollTo = function() {
if(this.length === 0) return;
jQuery.fn.smoothScrollTo = function () {
if (this.length === 0) return;
$('body').animate({
scrollTop: this.offset().top - 60 // Adjust to change final scroll position top margin
}, 800); // Adjust to change animations speed (ms)
@ -27,8 +27,8 @@ jQuery.fn.smoothScrollTo = function() {
};
// Making contains text expression not worry about casing
$.expr[":"].contains = $.expr.createPseudo(function(arg) {
return function( elem ) {
$.expr[":"].contains = $.expr.createPseudo(function (arg) {
return function (elem) {
return $(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
};
});
@ -42,7 +42,7 @@ $(function () {
});
// Chapter page list toggles
$('.chapter-toggle').click(function(e) {
$('.chapter-toggle').click(function (e) {
e.preventDefault();
$(this).toggleClass('open');
$(this).closest('.chapter').find('.inset-list').slideToggle(180);
@ -56,7 +56,7 @@ function elemExists(selector) {
}
// TinyMCE editor
if(elemExists('#html-editor')) {
if (elemExists('#html-editor')) {
var tinyMceOptions = require('./pages/page-form');
tinymce.init(tinyMceOptions);
}

View File

@ -1,4 +1,3 @@
module.exports = {
selector: '#html-editor',
content_css: [
@ -27,13 +26,13 @@ module.exports = {
{title: "Code Block", icon: "code", format: "pre"},
{title: "Inline Code", icon: "code", inline: "code"}
],
formats : {
alignleft : {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img', classes : 'align-left'},
aligncenter : {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img', classes : 'align-center'},
alignright : {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img', classes : 'align-right'},
formats: {
alignleft: {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img', classes: 'align-left'},
aligncenter: {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img', classes: 'align-center'},
alignright: {selector: 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img', classes: 'align-right'},
},
file_browser_callback: function(field_name, url, type, win) {
ImageManager.show(function(image) {
file_browser_callback: function (field_name, url, type, win) {
ImageManager.show(function (image) {
win.document.getElementById(field_name).value = image.url;
if ("createEvent" in document) {
var evt = document.createEvent("HTMLEvents");
@ -44,63 +43,63 @@ module.exports = {
}
});
},
paste_preprocess: function(plugin, args) {
paste_preprocess: function (plugin, args) {
var content = args.content;
if(content.indexOf('<img src="file://') !== -1) {
if (content.indexOf('<img src="file://') !== -1) {
args.content = '';
}
},
setup: function(editor) {
setup: function (editor) {
( function() {
(function () {
var wrap;
function hasTextContent( node ) {
return node && !! ( node.textContent || node.innerText );
function hasTextContent(node) {
return node && !!( node.textContent || node.innerText );
}
editor.on( 'dragstart', function() {
editor.on('dragstart', function () {
var node = editor.selection.getNode();
if ( node.nodeName === 'IMG' ) {
wrap = editor.dom.getParent( node, '.mceTemp' );
if (node.nodeName === 'IMG') {
wrap = editor.dom.getParent(node, '.mceTemp');
if ( ! wrap && node.parentNode.nodeName === 'A' && ! hasTextContent( node.parentNode ) ) {
if (!wrap && node.parentNode.nodeName === 'A' && !hasTextContent(node.parentNode)) {
wrap = node.parentNode;
}
}
} );
});
editor.on( 'drop', function( event ) {
editor.on('drop', function (event) {
var dom = editor.dom,
rng = tinymce.dom.RangeUtils.getCaretRangeFromPoint( event.clientX, event.clientY, editor.getDoc() );
rng = tinymce.dom.RangeUtils.getCaretRangeFromPoint(event.clientX, event.clientY, editor.getDoc());
// Don't allow anything to be dropped in a captioned image.
if ( dom.getParent( rng.startContainer, '.mceTemp' ) ) {
if (dom.getParent(rng.startContainer, '.mceTemp')) {
event.preventDefault();
} else if ( wrap ) {
} else if (wrap) {
event.preventDefault();
editor.undoManager.transact( function() {
editor.selection.setRng( rng );
editor.selection.setNode( wrap );
dom.remove( wrap );
} );
editor.undoManager.transact(function () {
editor.selection.setRng(rng);
editor.selection.setNode(wrap);
dom.remove(wrap);
});
}
wrap = null;
} );
} )();
});
})();
// Image picker button
editor.addButton('image-insert', {
title: 'My title',
icon: 'image',
tooltip: 'Insert an image',
onclick: function() {
window.ImageManager.showExternal(function(image) {
var html = '<a href="'+image.url+'" target="_blank">';
html += '<img src="'+image.thumbs.display+'" alt="'+image.name+'">';
onclick: function () {
window.ImageManager.showExternal(function (image) {
var html = '<a href="' + image.url + '" target="_blank">';
html += '<img src="' + image.thumbs.display + '" alt="' + image.name + '">';
html += '</a>';
editor.execCommand('mceInsertContent', false, html);
});
@ -108,10 +107,10 @@ module.exports = {
});
// Paste image-uploads
editor.on('paste', function(e) {
if(e.clipboardData) {
editor.on('paste', function (e) {
if (e.clipboardData) {
var items = e.clipboardData.items;
if (items){
if (items) {
for (var i = 0; i < items.length; i++) {
if (items[i].type.indexOf("image") !== -1) {
@ -128,14 +127,14 @@ module.exports = {
}
var id = "image-" + Math.random().toString(16).slice(2);
editor.execCommand('mceInsertContent', false, '<img src="/loading.gif" id="'+id+'">');
editor.execCommand('mceInsertContent', false, '<img src="/loading.gif" id="' + id + '">');
var remoteFilename = "image-" + Date.now() + "." + ext;
formData.append('file', file, remoteFilename);
formData.append('_token', document.querySelector('meta[name="token"]').getAttribute('content'));
xhr.open('POST', '/upload/image');
xhr.onload = function() {
xhr.onload = function () {
if (xhr.status === 200 || xhr.status === 201) {
var result = JSON.parse(xhr.responseText);
editor.dom.setAttrib(id, 'src', result.url);

View File

@ -32,6 +32,7 @@
font-weight: 300;
}
}
#image-manager .dropzone-container {
position: relative;
border: 3px dashed #DDD;
@ -52,7 +53,7 @@
width: (100%/6);
height: auto;
border: 1px solid #FFF;
transition: all cubic-bezier(.4,0,1,1) 160ms;
transition: all cubic-bezier(.4, 0, 1, 1) 160ms;
&.selected {
transform: scale3d(0.92, 0.92, 0.92);
}
@ -77,6 +78,7 @@
padding: 0 $-l;
border-left: 1px solid #DDD;
}
.image-manager-close {
position: absolute;
top: 0;
@ -84,6 +86,7 @@
margin: 0;
border-radius: 0;
}
.image-manager-list {
overflow-y: scroll;
flex: 1;
@ -97,9 +100,6 @@
flex: 1;
}
// Dropzone
/*
* The MIT License
@ -114,69 +114,104 @@
padding: $-xl $-m;
transition: all ease-in-out 120ms;
}
.dz-drag-hover .dz-message {
background-color: rgb(16, 126, 210);
color: #EEE;
}
@keyframes passing-through {
0% {
opacity: 0;
transform: translateY(40px); }
transform: translateY(40px);
}
30%, 70% {
opacity: 1;
transform: translateY(0px); }
transform: translateY(0px);
}
100% {
opacity: 0;
transform: translateY(-40px); } }
transform: translateY(-40px);
}
}
@keyframes slide-in {
0% {
opacity: 0;
transform: translateY(40px); }
transform: translateY(40px);
}
30% {
opacity: 1;
transform: translateY(0px); } }
transform: translateY(0px);
}
}
@keyframes pulse {
0% {
transform: scale(1); }
transform: scale(1);
}
10% {
transform: scale(1.1); }
transform: scale(1.1);
}
20% {
transform: scale(1); } }
.dropzone, .dropzone * {
box-sizing: border-box; }
transform: scale(1);
}
}
.dropzone, .dropzone * {
box-sizing: border-box;
}
.dz-preview {
position: relative;
display: inline-block;
vertical-align: top;
margin: 12px;
min-height: 80px; }
min-height: 80px;
}
.dz-preview:hover {
z-index: 1000; }
z-index: 1000;
}
.dz-preview:hover .dz-details {
opacity: 1; }
opacity: 1;
}
.dz-preview.dz-file-preview .dz-image {
border-radius: 4px;
background: #999;
background: linear-gradient(to bottom, #eee, #ddd); }
background: linear-gradient(to bottom, #eee, #ddd);
}
.dz-preview.dz-file-preview .dz-details {
opacity: 1; }
opacity: 1;
}
.dz-preview.dz-image-preview {
background: white; }
background: white;
}
.dz-preview.dz-image-preview .dz-details {
transition: opacity 0.2s linear; }
transition: opacity 0.2s linear;
}
.dz-preview .dz-remove {
font-size: 14px;
text-align: center;
display: block;
cursor: pointer;
border: none; }
border: none;
}
.dz-preview .dz-remove:hover {
text-decoration: underline; }
text-decoration: underline;
}
.dz-preview:hover .dz-details {
opacity: 1; }
opacity: 1;
}
.dz-preview .dz-details {
z-index: 20;
position: absolute;
@ -189,26 +224,42 @@
padding: 6px 3px;
text-align: center;
color: rgba(0, 0, 0, 0.9);
line-height: 150%; }
line-height: 150%;
}
.dz-preview .dz-details .dz-size {
margin-bottom: 0.5em;
font-size: 12px; }
font-size: 12px;
}
.dz-preview .dz-details .dz-filename {
white-space: nowrap; }
white-space: nowrap;
}
.dz-preview .dz-details .dz-filename:hover span {
border: 1px solid rgba(200, 200, 200, 0.8);
background-color: rgba(255, 255, 255, 0.8); }
background-color: rgba(255, 255, 255, 0.8);
}
.dz-preview .dz-details .dz-filename:not(:hover) {
overflow: hidden;
text-overflow: ellipsis; }
text-overflow: ellipsis;
}
.dz-preview .dz-details .dz-filename:not(:hover) span {
border: 1px solid transparent; }
border: 1px solid transparent;
}
.dz-preview .dz-details .dz-filename span, .dz-preview .dz-details .dz-size span {
background-color: rgba(255, 255, 255, 0.4);
padding: 0 0.4em;
border-radius: 3px; }
border-radius: 3px;
}
.dz-preview:hover .dz-image img {
filter: blur(8px); }
filter: blur(8px);
}
.dz-preview .dz-image {
border-radius: 4px;
overflow: hidden;
@ -216,14 +267,22 @@
height: 80px;
position: relative;
display: block;
z-index: 10; }
z-index: 10;
}
.dz-preview .dz-image img {
display: block; }
display: block;
}
.dz-preview.dz-success .dz-success-mark {
animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1); }
animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);
}
.dz-preview.dz-error .dz-error-mark {
opacity: 1;
animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1); }
animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);
}
.dz-preview .dz-success-mark, .dz-preview .dz-error-mark {
pointer-events: none;
opacity: 0;
@ -233,19 +292,29 @@
top: 50%;
left: 50%;
margin-left: -27px;
margin-top: -27px; }
margin-top: -27px;
}
.dz-preview .dz-success-mark svg, .dz-preview .dz-error-mark svg {
display: block;
width: 54px;
height: 54px; }
height: 54px;
}
.dz-preview.dz-processing .dz-progress {
opacity: 1;
transition: all 0.2s linear; }
transition: all 0.2s linear;
}
.dz-preview.dz-complete .dz-progress {
opacity: 0;
transition: opacity 0.4s ease-in; }
transition: opacity 0.4s ease-in;
}
.dz-preview:not(.dz-processing) .dz-progress {
animation: pulse 6s ease infinite; }
animation: pulse 6s ease infinite;
}
.dz-preview .dz-progress {
opacity: 1;
z-index: 1000;
@ -260,7 +329,9 @@
background: rgba(255, 255, 255, 0.9);
transform: scale(1);
border-radius: 8px;
overflow: hidden; }
overflow: hidden;
}
.dz-preview .dz-progress .dz-upload {
background: #333;
background: linear-gradient(to bottom, #666, #444);
@ -269,12 +340,18 @@
left: 0;
bottom: 0;
width: 0;
transition: width 300ms ease-in-out; }
transition: width 300ms ease-in-out;
}
.dz-preview.dz-error .dz-error-message {
display: block; }
display: block;
}
.dz-preview.dz-error:hover .dz-error-message {
opacity: 1;
pointer-events: auto; }
pointer-events: auto;
}
.dz-preview .dz-error-message {
pointer-events: none;
z-index: 1000;
@ -283,15 +360,17 @@
display: none;
opacity: 0;
transition: opacity 0.3s ease;
border-radius: 8px;
font-size: 13px;
top: 130px;
left: -10px;
width: 140px;
background: #be2626;
background: linear-gradient(to bottom, #be2626, #a92222);
padding: 0.5em 1.2em;
color: white; }
border-radius: 4px;
font-size: 11.5px;
line-height: 1.2;
top: 88px;
left: -26px;
width: 148px;
background: $negative;
padding: $-xs;
color: white;
}
.dz-preview .dz-error-message:after {
content: '';
position: absolute;
@ -301,4 +380,5 @@
height: 0;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid #be2626; }
border-bottom: 6px solid $negative;
}