add preview attached image, allow upload image from clipboard and drag & drp

This commit is contained in:
floatinghotpot 2015-11-13 11:13:54 +08:00
parent 41b23f88ae
commit eaf2afb44c
6 changed files with 225 additions and 2 deletions

View file

@ -3,6 +3,16 @@ template(name="cardAttachmentsPopup")
li
input.js-attach-file.hide(type="file" name="file" multiple)
a.js-computer-upload {{_ 'computer'}}
li
a.js-upload-clipboard-image {{_ 'clipboard'}}
template(name="previewClipboardImagePopup")
p {{_ "paste-or-dragdrop"}}
img.preview-clipboard-image()
button.primary.js-upload-pasted-image {{_ 'upload'}}
template(name="previewAttachedImagePopup")
img.preview-large-image.js-large-image-clicked(src="{{pathFor url}}")
template(name="attachmentDeletePopup")
p {{_ "attachment-delete-pop"}}
@ -15,7 +25,7 @@ template(name="attachmentsGalery")
.attachment-thumbnail
if isUploaded
if isImage
img.attachment-thumbnail-img(src="{{pathFor url}}")
img.attachment-thumbnail-img.js-preview-image(src="{{pathFor url}}")
else
span.attachment-thumbnail-ext= extension
else

View file

@ -20,6 +20,39 @@ Template.attachmentsGalery.events({
'click .js-remove-cover'() {
Cards.findOne(this.cardId).unsetCover();
},
'click .js-preview-image'(evt) {
Popup.open('previewAttachedImage').call(this, evt);
// when multiple thumbnails, if click one then another very fast,
// we might get a wrong width from previous img.
// when popup reused, onRendered() won't be called, so we cannot get there.
// here make sure to get correct size when this img fully loaded.
const img = $('img.preview-large-image')[0];
if (!img) return;
const rePosPopup = () => {
const w = img.width;
const h = img.height;
// if the image is too large, we resize & center the popup.
if (w > 300) {
$('div.pop-over').css({
width: (w + 20),
position: 'absolute',
left: (window.innerWidth - w)/2,
top: (window.innerHeight - h)/2,
});
}
};
const url = $(evt.currentTarget).attr('src');
if (img.src === url && img.complete)
rePosPopup();
else
img.onload = rePosPopup;
},
});
Template.previewAttachedImagePopup.events({
'click .js-large-image-clicked'(){
Popup.close();
},
});
Template.cardAttachmentsPopup.events({
@ -28,7 +61,7 @@ Template.cardAttachmentsPopup.events({
FS.Utility.eachFile(evt, (f) => {
const file = new FS.File(f);
file.boardId = card.boardId;
file.cardId = card._id;
file.cardId = card._id;
Attachments.insert(file);
Popup.close();
@ -38,4 +71,48 @@ Template.cardAttachmentsPopup.events({
tpl.find('.js-attach-file').click();
evt.preventDefault();
},
'click .js-upload-clipboard-image': Popup.open('previewClipboardImage'),
});
let pastedResults = null;
Template.previewClipboardImagePopup.onRendered(() => {
// we can paste image from clipboard
$(document.body).pasteImageReader((results) => {
if (results.dataURL.startsWith('data:image/')) {
$('img.preview-clipboard-image').attr('src', results.dataURL);
pastedResults = results;
}
});
// we can also drag & drop image file to it
$(document.body).dropImageReader((results) => {
if (results.dataURL.startsWith('data:image/')) {
$('img.preview-clipboard-image').attr('src', results.dataURL);
pastedResults = results;
}
});
});
Template.previewClipboardImagePopup.events({
'click .js-upload-pasted-image'() {
const results = pastedResults;
if (results && results.file) {
const card = this;
const file = new FS.File(results.file);
if (!results.name) {
// if no filename, it's from clipboard. then we give it a name, with ext name from MIME type
if (typeof results.file.type === 'string') {
file.name(results.file.type.replace('image/', 'clipboard.'));
}
}
file.updatedAt(new Date());
file.boardId = card.boardId;
file.cardId = card._id;
Attachments.insert(file);
pastedResults = null;
$(document.body).pasteImageReader(() => {});
Popup.close();
}
},
});

View file

@ -45,3 +45,14 @@
display: block
box-shadow: 0 1px 2px rgba(0,0,0,.2)
.preview-large-image
max-width: 1000px
display: block
box-shadow: 0 1px 2px rgba(0,0,0,.2)
.preview-clipboard-image
width: 280px
height: 200px
display: block
border: 1px solid black
box-shadow: 0 1px 2px rgba(0,0,0,.2)

62
client/lib/dropImage.js Normal file
View file

@ -0,0 +1,62 @@
/* eslint-disable */
// ------------------------------------------------------------------------
// Created by STRd6
// MIT License
// https://github.com/distri/jquery-image_reader/blob/master/drop.coffee.md
//
// Raymond re-write it to javascript
(function($) {
$.event.fix = (function(originalFix) {
return function(event) {
event = originalFix.apply(this, arguments);
if (event.type.indexOf('drag') === 0 || event.type.indexOf('drop') === 0) {
event.dataTransfer = event.originalEvent.dataTransfer;
}
return event;
};
})($.event.fix);
const defaults = {
callback: $.noop,
matchType: /image.*/,
};
return $.fn.dropImageReader = function(options) {
if (typeof options === 'function') {
options = {
callback: options,
};
}
options = $.extend({}, defaults, options);
const stopFn = function(event) {
event.stopPropagation();
return event.preventDefault();
};
return this.each(function() {
const element = this;
$(element).bind('dragenter dragover dragleave', stopFn);
return $(element).bind('drop', function(event) {
stopFn(event);
const files = event.dataTransfer.files;
for(let i=0; i<files.length; i++) {
const f = files[i];
if(f.type.match(options.matchType)) {
const reader = new FileReader();
reader.onload = function(evt) {
return options.callback.call(element, {
dataURL: evt.target.result,
event: evt,
file: f,
name: f.name,
});
};
reader.readAsDataURL(f);
return;
}
}
});
});
};
})(jQuery);

57
client/lib/pasteImage.js Normal file
View file

@ -0,0 +1,57 @@
/* eslint-disable */
// ------------------------------------------------------------------------
// Created by STRd6
// MIT License
// https://github.com/distri/jquery-image_reader/blob/master/paste.coffee.md
//
// Raymond re-write it to javascript
(function($) {
$.event.fix = (function(originalFix) {
return function(event) {
event = originalFix.apply(this, arguments);
if (event.type.indexOf('copy') === 0 || event.type.indexOf('paste') === 0) {
event.clipboardData = event.originalEvent.clipboardData;
}
return event;
};
})($.event.fix);
const defaults = {
callback: $.noop,
matchType: /image.*/,
};
return $.fn.pasteImageReader = function(options) {
if (typeof options === 'function') {
options = {
callback: options,
};
}
options = $.extend({}, defaults, options);
return this.each(function() {
const element = this;
return $(element).bind('paste', function(event) {
const types = event.clipboardData.types;
const items = event.clipboardData.items;
for(let i=0; i<types.length; i++) {
if(types[i].match(options.matchType) || items[i].type.match(options.matchType)) {
const f = items[i].getAsFile();
const reader = new FileReader();
reader.onload = function(evt) {
return options.callback.call(element, {
dataURL: evt.target.result,
event: evt,
file: f,
name: f.name,
});
};
reader.readAsDataURL(f);
return;
}
}
});
});
};
})(jQuery);

View file

@ -87,6 +87,7 @@
"changePermissionsPopup-title": "Change Permissions",
"click-to-star": "Click to star this board.",
"click-to-unstar": "Click to unstar this board.",
"clipboard" : "Clipboard or drag & drop",
"close": "Close",
"close-board": "Close Board",
"close-board-pop": "You can re-open the board by clicking the “Boards” menu from the header, selecting “View Closed Boards”, finding the board and clicking “Re-open”.",
@ -183,6 +184,10 @@
"page-maybe-private": "This page may be private. You may be able to view it by <a href='%s'>logging in</a>.",
"page-not-found": "Page not found.",
"password": "Password",
"paste-or-dragdrop": "Ctrl-V to paste, or drag & drop image file to it (image only)",
"preview": "Preview",
"previewClipboardImagePopup-title": "Preview",
"previewAttachedImagePopup-title": "Preview",
"private": "Private",
"private-desc": "This board is private. Only people added to the board can view and edit it.",
"profile": "Profile",
@ -222,6 +227,7 @@
"title": "Title",
"unassign-member": "Unassign member",
"unsaved-description": "You have an unsaved description.",
"upload": "Upload",
"upload-avatar": "Upload an avatar",
"uploaded-avatar": "Uploaded an avatar",
"username": "Username",