Last active
April 6, 2017 19:47
-
-
Save trevorhreed/4055bcf7b354f1b2750ddda3aae5cc36 to your computer and use it in GitHub Desktop.
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
// #region MISC | |
/* | |
Usage: | |
<image-input content-id="{{show.id}}" image-type="Show" ng-model="show.images"></image-input> | |
<image-input content-id="{{show.id}}" image-type="ListImage" ng-model="show.listImage"></image-input> | |
*/ | |
ngm.value('ImageTypeInfo', { | |
'Document': { | |
type: 'Document', | |
label: 'Post (1280x720)', | |
multiple: false, | |
minWidth: 1280, | |
minHeight: 720, | |
fixedRatio: true | |
}, | |
'Show': { | |
type: 'Show', | |
label: 'Show (1280x720)', | |
multiple: true, | |
minWidth: 1280, | |
minHeight: 720, | |
fixedRatio: true | |
}, | |
'Episode': { | |
type: 'Episode', | |
label: 'Episode (1280x720)', | |
multiple: true, | |
minWidth: 1280, | |
minHeight: 720, | |
fixedRatio: true | |
}, | |
'CastMember': { | |
type: 'CastMember', | |
label: 'Cast (1280x720)', | |
multiple: true, | |
minWidth: 1280, | |
minHeight: 720, | |
fixedRatio: true | |
}, | |
'Banner': { | |
type: 'Banner', | |
label: 'Banner (1280x720)', | |
multiple: false, | |
minWidth: 1280, | |
minHeight: 720, | |
fixedRatio: true | |
}, | |
'Logo': { | |
type: 'Logo', | |
label: 'Logo (???)', | |
multiple: false | |
}, | |
'Background': { | |
type: 'Background', | |
label: 'Background (???)', | |
multiple: false | |
}, | |
'ListImage': { | |
type: 'ListImage', | |
label: 'List (1280x720)', | |
multiple: false, | |
minWidth: 1280, | |
minHeight: 720, | |
fixedRatio: true | |
}, | |
'SquareImage': { | |
type: 'SquareImage', | |
label: 'Square (1400x1400)', | |
multiple: false, | |
minWidth: 1400, | |
minHeight: 1400, | |
fixedRatio: true | |
}, | |
'GalleryPicture': { | |
type: 'GalleryPicture', | |
label: 'Gallery (min. height 480px)', | |
multiple: false, | |
minHeight: 480 | |
} | |
}); | |
ngm.directive('ngError', function($parse){ | |
return { | |
restrict: 'A', | |
compile: ($element, attr)=>{ | |
let fn = $parse(attr['ngError']); | |
return (scope, element, attr)=>{ | |
element.on('error', function(event) { | |
scope.$apply(function() { | |
fn(scope, {$event:event}); | |
}); | |
}); | |
}; | |
} | |
}; | |
}); | |
// #endregion | |
// #region API | |
ngm.service('imageApi', function(mdeApi){ | |
return mdeApi.createApi({ | |
prep(images){ | |
if(!images) return []; | |
if(!Array.isArray(images)) images = [images]; | |
return images.map((x)=>{ | |
x.state = x.state || 'keep'; | |
x.originalUrl = x.originalUrl || (x.images && x.images[0] || {}).url; | |
x.thumbUrl = x.thumbUrl || (x.images && x.images[1] || {}).url; | |
return x; | |
}).sort((a, b)=>{ | |
if(a.ordinal < b.ordinal) return -1; | |
if(a.ordinal > b.ordinal) return 1; | |
return 0; | |
}); | |
}, | |
get(contentId, imageTypeInfo){ | |
let images = []; | |
mdeApi.begin(); | |
if(imageTypeInfo.multiple){ | |
mdeApi(`GET /Image/GetAllImagesForContent?contentId=${contentId}&imageType=${imageTypeInfo.type}`) | |
.then(x => images = x); | |
}else{ | |
mdeApi(`GET /Image/GetImage?contentId=${contentId}&imageType=${imageTypeInfo.type}`) | |
.then(x => images = [x]); | |
} | |
return mdeApi.end().then(()=>{ | |
return this.prep(images); | |
}) | |
}, | |
save(images, contentId){ | |
mdeApi.begin(); | |
if(images){ | |
let imagesToUpdate = []; | |
if(!Array.isArray(images)) images = [images]; | |
images | |
.forEach((x)=>{ | |
if(x.state === 'add'){ | |
var data = new FormData(); | |
data.append('Image', x.upload.getFile()); | |
data.append('contentId', contentId); | |
data.append('imageId', x.imageId); | |
data.append('imageType', x.imageType); | |
data.append('ordinal', x.ordinal); | |
mdeApi(`POST /Image/UploadImage`, data, true); | |
}else if(x.state === 'delete'){ | |
mdeApi(`POST /Image/RemoveImage?contentId=${contentId}&imageType=${x.imageType}&imageId=${x.imageId}`); | |
}else{ | |
imagesToUpdate.push(x); | |
} | |
}); | |
if(imagesToUpdate.length > 1){ | |
mdeApi(`POST /Image/UpdateContentImageOrder`, { images: imagesToUpdate }); | |
} | |
} | |
return mdeApi.end(); | |
} | |
}) | |
}) | |
// #endregion | |
// #region Input | |
ngm.directive('imageInput', function(ImageTypeInfo, imageApi){ | |
return { | |
require: '?ngModel', | |
scope: { imageType: '@' }, | |
template: html` | |
<div layout="row" layout-align="start end"> | |
<image-preview images="data.images" image-type-info="data.imageTypeInfo" image-lightbox="data.images"></image-preview> | |
<image-selector images="data.images" image-type-info="data.imageTypeInfo" on-change="onChanged($event)"></image-selector> | |
</div> | |
<em class="md-body-2">{{data.imageTypeInfo.label || 'Unknown Image Type: ' + imageType}}</em> | |
`, | |
link: (scope, element, attrs, ngModelCtrl)=>{ | |
let imageTypeInfo = ImageTypeInfo[scope.imageType]; | |
if(!imageTypeInfo) return element.text('Error: Unknown Image Type'); | |
scope.data = { imageTypeInfo }; | |
if(ngModelCtrl){ | |
ngModelCtrl.$render = () => { | |
scope.data.images = imageApi.prep(ngModelCtrl.$viewValue); | |
} | |
} | |
scope.onChanged = ($event) => { | |
if(ngModelCtrl){ | |
let images = angular.copy($event.images); | |
if(!imageTypeInfo.multiple && Array.isArray(images)){ | |
images = images.shift(); | |
} | |
ngModelCtrl.$setViewValue(images); | |
ngModelCtrl.$render(); | |
} | |
} | |
} | |
} | |
}); | |
// #endregion | |
// #region Preview | |
ngm.directive('imagePreview', function(){ | |
return { | |
restrict: 'E', | |
scope: { | |
images: '<', | |
imageTypeInfo: '<' | |
}, | |
template: html` | |
<reference-point ng-if="imageTypeInfo.multiple"></reference-point> | |
<img ng-src="{{image1.thumbUrl || '-1'}}" ng-error="loadDefault($event)" ng-if="images" /> | |
<img ng-src="{{image2.thumbUrl || '-2'}}" ng-error="loadDefault($event)" ng-if="images && imageTypeInfo && imageTypeInfo.multiple" /> | |
`, | |
link: (scope, element, attrs) => { | |
scope.$watch('images', (newImages)=>{ | |
if(!newImages){ | |
scope.image1 = null; | |
scope.image2 = null; | |
}else{ | |
let images = angular | |
.copy(newImages) | |
.filter(i => i.state !== 'delete') | |
.sort((a, b)=>{ | |
if(a.ordinal < b.ordinal) return -1; | |
if(a.ordinal > b.ordinal) return 1; | |
return 0; | |
}); | |
scope.image1 = images.shift(); | |
scope.image2 = images.shift(); | |
} | |
}); | |
let height = 200; | |
scope.$watch('imageTypeInfo', (imageTypeInfo)=>{ | |
if(!imageTypeInfo) return; | |
let scaleWidth = imageTypeInfo.minWidth || imageTypeInfo.exactWidth; | |
let scaleHeight = imageTypeInfo.minHeight || imageTypeInfo.exactHeight; | |
if(scaleWidth && scaleHeight){ | |
let ratio = (scaleWidth / scaleHeight); | |
height = 200 / ratio; | |
} | |
}); | |
scope.loadDefault = ($event) => { | |
$event.target.src = `http://placehold.it/200x${height}?text=No Image`; | |
} | |
} | |
} | |
}); | |
// #endregion | |
// #region Lightbox | |
ngm.directive('imageLightbox', function($compile, $timeout){ | |
let lightboxTemplate = html` | |
<image-lightbox> | |
<img ng-src="{{images[index].originalUrl}}" | |
ng-click="moveRightIfExists()" | |
ng-error="loadDefault($event)" | |
md-swipe-left="moveRight()" | |
md-swipe-right="moveLeft()" /> | |
<div class="tools"> | |
<md-button class="md-icon-button mdi-chevron-left" ng-click="moveLeft()" ng-if="images.length > 1"></md-button> | |
<md-button class="md-icon-button mdi-close" ng-click="close()"></md-button> | |
<md-button class="md-icon-button mdi-chevron-right" ng-click="moveRight()" ng-if="images.length > 1"></md-button> | |
</div> | |
</image-lightbox> | |
`; | |
return { | |
restrict: 'A', | |
link: (scope, element, attrs) => { | |
let lightboxScope = scope.$new(); | |
lightboxScope.index = 0; | |
scope.$watch(attrs.imageLightbox, i => lightboxScope.images = i); | |
let lightbox; | |
element.on('click', (e) => { | |
lightbox = $compile(lightboxTemplate)(lightboxScope); | |
let image = $('img', lightbox); | |
let changeImage = (direction) => { | |
image.fadeOut(()=>{ | |
lightboxScope.index += direction; | |
if(lightboxScope.index < 0){ | |
lightboxScope.index = lightboxScope.images.length - 1; | |
}else if(lightboxScope.index >= lightboxScope.images.length){ | |
lightboxScope.index = 0; | |
} | |
$timeout(x => image.fadeIn()); | |
}); | |
} | |
lightboxScope.close = x => lightbox.fadeOut(x => lightbox.remove()); | |
lightboxScope.left = x => changeImage(-1); | |
lightboxScope.moveRight = x => changeImage(-1); | |
lightboxScope.moveRightIfExists = x => lightboxScope.images.length > 1 && changeImage(1); | |
lightbox.hide().appendTo(document.body).fadeIn(); | |
}); | |
} | |
} | |
}); | |
// #endregion | |
// #region Selector | |
ngm.directive('imageSelector', function($mdDialog, $timeout, imageApi){ | |
let dialog = { | |
fullscreen: true, | |
autoWrap: false, | |
template: html` | |
<md-dialog class="image-selector"> | |
<md-toolbar class="md-toolbar-tools"> | |
<h2><span>{{title}}</span></h2> | |
</md-toolbar> | |
<md-dialog-content class="md-dialog-content"> | |
<div relative layout="column"> | |
<mde-loader active="readingImage"></mde-loader> | |
<em class="md-body-2">{{imageTypeInfo.label}}</em> | |
<md-button class="md-primary md-raised" | |
image-browser-trigger="imageTypeInfo" | |
image-type-info="imageTypeInfo" | |
on-load="onImageLoaded($event)" | |
on-error="onImageLoadError($event)" | |
ng-click="imageLoadError = ''"> | |
Browse Images | |
</md-button> | |
<em class="md-body-1" ng-if="imageLoadError">{{imageLoadError}}</em> | |
</div> | |
<h2 class="md-title">{{imageTypeInfo.multiple ? 'Images' : 'Image'}}</h2> | |
<div class="images"> | |
<div ng-repeat="image in images | filter:isNotDeleted | orderBy:'ordinal'" | |
data-image-id="{{image.imageId}}" class="image" layout="row" layout-align="start start"> | |
<md-icon md-font-icon="mdi-drag" class="md-primary" hide show-gt-sm ng-if="imageTypeInfo.multiple"></md-icon> | |
<img ng-src="{{image.thumbUrl}}" width="200" class="preview" /> | |
<div class="actions" layout="column" layout-gt-md="row" layout-align="center center" ng-if="imageTypeInfo.multiple"> | |
<md-button class="md-icon-button md-primary mdi-arrow-up" ng-click="up(image)" ng-disabled="$first"> | |
<md-tooltip>Move Up</md-tooltip> | |
</md-button> | |
<md-button class="md-icon-button md-primary mdi-arrow-down" ng-click="down(image)" ng-disabled="$last"> | |
<md-tooltip>Move Down</md-tooltip> | |
</md-button> | |
<md-button class="md-icon-button md-warn mdi-delete" ng-click="del(image)"> | |
<md-tooltip>Delete</md-tooltip> | |
</md-button> | |
</div> | |
</div> | |
</div> | |
</md-dialog-content> | |
<md-dialog-actions> | |
<md-button ng-click="cancel()">Cancel</md-button> | |
<md-button ng-click="ok()" class="md-primary">OK</md-button> | |
</md-dialog-actions> | |
</md-dialog> | |
`, | |
controller: function($scope, images, imageTypeInfo){ | |
$scope.images = images; | |
let makeSortable = ()=>{ | |
$timeout(()=>{ | |
$('.images').sortable({ | |
handle: 'md-icon', | |
cursor: 'move', | |
stop: ()=>{ | |
let imageIndex = {}; | |
$('.images .image').each((index, el)=>{ | |
let id = $(el).attr('data-image-id'); | |
imageIndex[id] = index; | |
}); | |
let images = angular.copy($scope.images); | |
for (let i = 0; i < images.length; i++) { | |
let id = images[i].imageId; | |
images[i].ordinal = imageIndex[id]; | |
} | |
$timeout(x => $scope.images = images); | |
} | |
}); | |
}); | |
} | |
makeSortable(); | |
$scope.imageTypeInfo = imageTypeInfo; | |
$scope.title = imageTypeInfo.multiple ? 'Select Images' : 'Select Image'; | |
$scope.isNotDeleted = (image) => { | |
return image.state !== 'delete'; | |
} | |
$scope.del = (image) => { | |
if(image.state === 'add') $scope.images.splice($scope.images.indexOf(image), 1); | |
else image.state = 'delete'; | |
} | |
$scope.up = function(image) { | |
if (image.ordinal === 0) return; | |
image.ordinal--; | |
$scope.images.forEach(function(item) { | |
if (item.ordinal === image.ordinal && item.imageId !== image.imageId) { | |
item.ordinal++; | |
} | |
}); | |
} | |
$scope.down = function(image) { | |
if (image.ordinal === $scope.images.length - 1) return; | |
image.ordinal++; | |
$scope.images.forEach(function(item) { | |
if (item.ordinal === image.ordinal && item.imageId !== image.imageId) { | |
item.ordinal--; | |
} | |
}); | |
} | |
$scope.onImageLoadError = ($event) => { | |
$scope.imageLoadError = 'Unknown Error.'; | |
} | |
$scope.onImageLoaded = ($event) => { | |
$scope.newImage = { | |
imageType: imageTypeInfo.type, | |
imageId: imageApi.newId(), | |
state: 'add', | |
upload: $event.upload, | |
originalUrl: $event.upload.url, | |
thumbUrl: $event.upload.url, | |
ordinal: $scope.images.length | |
}; | |
if(imageTypeInfo.multiple){ | |
$scope.images.push($scope.newImage); | |
}else{ | |
$scope.images = [$scope.newImage]; | |
} | |
$scope.newImage = null; | |
makeSortable(); | |
} | |
$scope.cancel = $mdDialog.cancel; | |
$scope.ok = () => { | |
$mdDialog.hide($scope.images); | |
} | |
} | |
}; | |
return { | |
scope: { | |
images: '<', | |
imageTypeInfo: '<', | |
onChange: '&?' | |
}, | |
template: html` | |
<md-button class="md-icon-button mdi-upload" ng-click="open()"> | |
<md-tooltip>Upload Image</md-tooltip> | |
</md-button> | |
`, | |
link: (scope, element, attrs)=>{ | |
scope.open = () => { | |
let dialogInstance = angular.extend({ | |
locals: { | |
images: angular.copy(scope.images || {}), | |
imageTypeInfo: scope.imageTypeInfo | |
} | |
}, dialog); | |
$mdDialog | |
.show(dialogInstance) | |
.then(images => scope.onChange && scope.onChange({'$event': {images}})); | |
} | |
} | |
} | |
}); | |
// #endregion | |
// #region Dropzone | |
ngm.directive('imageBrowserTrigger', function($timeout, $q, ImageTypeInfo){ | |
return { | |
restrict: 'A', | |
scope: { | |
imageTypeInfo: '<', | |
onLoad: '&', | |
onError: '&' | |
}, | |
link: (scope, element, attrs)=>{ | |
let uploader = document.createElement('INPUT'); | |
uploader.type = 'file'; | |
if(scope.imageTypeInfo.multiple) uploader.setAttribute('multiple', 'multiple'); | |
uploader.addEventListener('change', e => loadFile(uploader.files[0])); | |
element.on('click', x => uploader.click()); | |
let {exactWidth, exactHeight, minWidth, minHeight, fixedRatio} = scope.imageTypeInfo || {}; | |
let validateImage = (url) => { | |
let hasRequirements = exactWidth || exactHeight || minWidth || minHeight; | |
if(!hasRequirements) return $q.when(); | |
return $q((resolve, reject)=>{ | |
let image = new Image(); | |
image.onload = x => resolve(image); | |
image.src = url; | |
}).then((image)=>{ | |
if(exactWidth && exactWidth !== image.width) throw `Invalid width: must be ${exactWidth} pixels.`; | |
if(exactHeight && exactHeight !== image.height) throw `Invalid height: must be ${exactHeight} pixels.`; | |
if(minWidth && minWidth > image.width) throw `Invalid width: must be at least ${minWidth} pixels.`; | |
if(minHeight && minHeight > image.height) throw `Invalid height: must be at least ${minHeight} pixels.`; | |
let imageRatio = (image.width / image.height); | |
if(fixedRatio && exactWidth && exactHeight && (exactWidth / exactHeight) !== imageRatio) throw `Invalid aspect ratio: must be ${exactWidth}x${exactHeight}.`; | |
if(fixedRatio && minWidth && minHeight && (minWidth / minHeight) !== imageRatio) throw `Invalid aspect ratio: must be ${minWidth}x${minHeight}.`; | |
return; | |
}); | |
} | |
let loadFile = (file) => { | |
$timeout(()=>{ | |
scope.loading = $q((resolve, reject)=>{ | |
let reader = new FileReader(); | |
reader.onload = resolve; | |
reader.readAsDataURL(file); | |
}).then((e)=>{ | |
let url = e.target.result; | |
validateImage(url) | |
.then(()=>{ | |
scope.image = { getFile(){ return file }, url }; | |
scope.onLoad({'$event': { upload: scope.image }}); | |
}) | |
.catch((err)=>{ | |
scope.image = null; | |
scope.onError({'$event': err}); | |
}); | |
}); | |
}); | |
} | |
} | |
} | |
}); | |
ngm.directive('imageDropzone', function($timeout, $q, ImageTypeInfo){ | |
return { | |
scope: { | |
imageTypeInfo: '<', | |
width: '@', | |
height: '@', | |
text: '@', | |
onLoad: '&', | |
onError: '&' | |
}, | |
template: html` | |
<mde-loader active="loading"></mde-loader> | |
<inner-text ng-if="!image">{{message}}</inner-text> | |
<img ng-src="{{image.url}}" ng-if="image" /> | |
`, | |
link: (scope, element, attrs)=>{ | |
let {exactWidth, exactHeight, minWidth, minHeight, fixedRatio} = scope.imageTypeInfo || {}; | |
scope.message = scope.text; | |
element.width(scope.width); | |
element.height(scope.height); | |
let el = element[0]; | |
let uploader = document.createElement('INPUT'); | |
uploader.type = 'file'; | |
let reset = () => { | |
scope.image = null; | |
scope.message = scope.text; | |
} | |
let validateImage = (url) => { | |
let hasRequirements = exactWidth || exactHeight || minWidth || minHeight; | |
if(!hasRequirements) return $q.when(); | |
return $q((resolve, reject)=>{ | |
let image = new Image(); | |
image.onload = x => resolve(image); | |
image.src = url; | |
}).then((image)=>{ | |
if(exactWidth && exactWidth !== image.width) throw `Invalid width: must be ${exactWidth} pixels.`; | |
if(exactHeight && exactHeight !== image.height) throw `Invalid height: must be ${exactHeight} pixels.`; | |
if(minWidth && minWidth > image.width) throw `Invalid width: must be at least ${minWidth} pixels.`; | |
if(minHeight && minHeight > image.height) throw `Invalid height: must be at least ${minHeight} pixels.`; | |
let imageRatio = (image.width / image.height); | |
if(fixedRatio && exactWidth && exactHeight && (exactWidth / exactHeight) !== imageRatio) throw `Invalid aspect ratio: must be ${exactWidth}x${exactHeight}.`; | |
if(fixedRatio && minWidth && minHeight && (minWidth / minHeight) !== imageRatio) throw `Invalid aspect ratio: must be ${minWidth}x${minHeight}.`; | |
return; | |
}); | |
} | |
let loadFile = (file) => { | |
$timeout(()=>{ | |
scope.loading = $q((resolve, reject)=>{ | |
let reader = new FileReader(); | |
reader.onload = resolve; | |
reader.readAsDataURL(file); | |
}).then((e)=>{ | |
let url = e.target.result; | |
validateImage(url) | |
.then(()=>{ | |
scope.image = { file, url }; | |
scope.onLoad({'$event': { | |
upload: scope.image, | |
reset | |
}}); | |
}) | |
.catch((err)=>{ | |
scope.image = null; | |
scope.message = err; | |
scope.onError({'$event': err}); | |
}); | |
}); | |
}); | |
} | |
uploader.addEventListener('change', e => loadFile(uploader.files[0])); | |
el.addEventListener('click', e => uploader.click()); | |
el.addEventListener('dragleave', (e) => { | |
$timeout(x => element.removeClass('dropping')); | |
}); | |
el.addEventListener('dragover', (e)=>{ | |
e.preventDefault(); | |
let isDraggingImages = e.dataTransfer.types.indexOf('Files') !== -1 && e.dataTransfer.items[0].type.indexOf('image/') === 0; | |
if(isDraggingImages){ | |
$timeout(()=>{ | |
e.dataTransfer.dropEffect = 'copy'; | |
element.addClass('dropping'); | |
}); | |
} | |
}); | |
el.addEventListener('drop', (e)=>{ | |
e.preventDefault(); | |
let file = e.dataTransfer.files[0]; | |
$timeout(()=>{ | |
element.removeClass('dropping'); | |
loadFile(file); | |
}); | |
}); | |
} | |
} | |
}) | |
// #endregion | |
/* | |
SCSS | |
image-input{ | |
display:inline-block; | |
margin:18px 0; | |
padding-left:12px; | |
em{ | |
padding-left:6px; | |
color:rgba(0, 0, 0, .5); | |
} | |
} | |
image-preview{ | |
display:inline-block; | |
position:relative; | |
cursor:pointer; | |
img{ | |
border:solid 1px #aaa; | |
padding:5px; | |
background:#fff; | |
box-shadow:0 0 3px #aaa; | |
width:200px; | |
} | |
reference-point + img{ | |
position:absolute; | |
top:10px; | |
left:10px; | |
} | |
img + img{ | |
margin:0 10px 10px 0; | |
} | |
} | |
image-lightbox{ | |
position:fixed; | |
top:0;left:0;right:0;bottom:0; | |
z-index:100; | |
background:rgba(0, 0, 0, .9); | |
display:flex; | |
flex-direction:column; | |
align-items:center; | |
justify-content:center; | |
img{ | |
padding:5px; | |
border:solid 1px #aaa; | |
background:#fff; | |
box-shadow:0 0 3px #000; | |
max-width:100%; | |
max-height:100%; | |
box-sizing:border-box; | |
} | |
.tools{ | |
position:absolute; | |
left:0;right:0;bottom:0; | |
display:flex; | |
flex-direction:row; | |
justify-content:center; | |
background:rgba(0, 0, 0, .5); | |
.md-button.md-icon-button::before{ | |
color:#fff; | |
} | |
} | |
} | |
md-dialog.image-selector{ | |
em{ | |
color:rgba(0, 0, 0, .5); | |
} | |
[image-browser-trigger]{ | |
margin-left:0; | |
margin-right:0; | |
} | |
.images{ | |
.image{ | |
margin-bottom:12px; | |
img.preview{ | |
border:solid 1px #aaa; | |
padding:5px; | |
background:#fff; | |
box-shadow:0 0 3px #aaa; | |
} | |
.actions{ | |
.md-icon-button{ | |
margin:0 !important; | |
} | |
} | |
} | |
} | |
} | |
image-dropzone{ | |
position:relative; | |
display:block; | |
border:solid 1px #aaa; | |
padding:5px; | |
background:#fff; | |
box-shadow:0 0 3px #aaa; | |
text-align:center; | |
cursor:pointer; | |
transition: all 300ms ease; | |
&.dropping{ | |
background:#eee; | |
} | |
&::before{ | |
content: ''; | |
display: inline-block; | |
height:100%; | |
vertical-align: middle; | |
margin-right: -0.25em; | |
} | |
inner-text{ | |
display:inline-block; | |
vertical-align:middle; | |
} | |
img{ | |
max-width:100%; | |
max-height:100%; | |
display:inline-block; | |
vertical-align:middle; | |
} | |
} | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment