mirror of
https://github.com/EGroupware/egroupware.git
synced 2025-01-27 16:29:22 +01:00
Drag & drop files from user's system onto nextmatch row uploads & links file to that row
This commit is contained in:
parent
4e2843b4a1
commit
6028ac8eca
@ -100,6 +100,11 @@ var et2_nextmatch = et2_DOMWidget.extend([et2_IResizeable, et2_IInput],
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "JS code which gets executed when rows are selected. Can also be a app.appname.func(selected) style method"
|
"description": "JS code which gets executed when rows are selected. Can also be a app.appname.func(selected) style method"
|
||||||
},
|
},
|
||||||
|
"onfiledrop": {
|
||||||
|
"name": "onFileDrop",
|
||||||
|
"type": "js",
|
||||||
|
"description": "JS code that gets executed when a _file_ is dropped on a row. Other drop interactions are handled by the action system. Return false to prevent the default link action."
|
||||||
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"name": "Settings",
|
"name": "Settings",
|
||||||
"type": "any",
|
"type": "any",
|
||||||
@ -213,6 +218,46 @@ var et2_nextmatch = et2_DOMWidget.extend([et2_IResizeable, et2_IInput],
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
doLoadingFinished: function() {
|
||||||
|
this._super.apply(this, arguments);
|
||||||
|
|
||||||
|
// Register handler for dropped files, if possible
|
||||||
|
if(this.options.settings.row_id)
|
||||||
|
{
|
||||||
|
// Appname should be first part of the template name
|
||||||
|
var split = this.options.template.split('.');
|
||||||
|
var appname = split[0];
|
||||||
|
|
||||||
|
// Check link registry
|
||||||
|
if(this.egw().link_get_registry(appname))
|
||||||
|
{
|
||||||
|
var self = this;
|
||||||
|
// Register a handler
|
||||||
|
$j('table.egwGridView_grid',this.div)
|
||||||
|
.on('dragenter','tr',function(e) {
|
||||||
|
var row = self.controller._getIndexEntry($j(this).index());
|
||||||
|
if(!row || !row.uid)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
e.stopPropagation(); e.preventDefault();
|
||||||
|
self.controller._selectionMgr.setFocused(row.uid,true);
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
.on('dragexit','tr', function(e) {
|
||||||
|
self.controller._selectionMgr.setFocused();
|
||||||
|
})
|
||||||
|
.on('dragover','tr',false).attr("dropzone","copy")
|
||||||
|
|
||||||
|
.on('drop', 'tr',function(e) {
|
||||||
|
self.handle_drop(e,this);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -436,8 +481,8 @@ var et2_nextmatch = et2_DOMWidget.extend([et2_IResizeable, et2_IInput],
|
|||||||
*/
|
*/
|
||||||
onselect: function(action,senders) {
|
onselect: function(action,senders) {
|
||||||
// Execute the JS code connected to the event handler
|
// Execute the JS code connected to the event handler
|
||||||
if (this.options.onselect)
|
if (this.options.onselect)
|
||||||
{
|
{
|
||||||
if (typeof this.options.onselect == "string" &&
|
if (typeof this.options.onselect == "string" &&
|
||||||
this.options.onselect.substr(0,4) == "app." && window.app)
|
this.options.onselect.substr(0,4) == "app." && window.app)
|
||||||
{
|
{
|
||||||
@ -451,12 +496,12 @@ var et2_nextmatch = et2_DOMWidget.extend([et2_IResizeable, et2_IInput],
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exectute the legacy JS code
|
// Exectute the legacy JS code
|
||||||
else if (!(et2_compileLegacyJS(this.options.onselect, this, this.div))())
|
else if (!(et2_compileLegacyJS(this.options.onselect, this, this.div))())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1180,6 +1225,107 @@ var et2_nextmatch = et2_DOMWidget.extend([et2_IResizeable, et2_IInput],
|
|||||||
* Actions are handled by the controller, so ignore these
|
* Actions are handled by the controller, so ignore these
|
||||||
*/
|
*/
|
||||||
set_actions: function(actions) {},
|
set_actions: function(actions) {},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a different / additional handler for dropped files.
|
||||||
|
*
|
||||||
|
* File dropping doesn't work with the action system, so we handle it in the
|
||||||
|
* nextmatch by linking automatically to the target row. This allows an additional handler.
|
||||||
|
* It should accept a row UID and a File[], and return a boolean Execute the default (link) action
|
||||||
|
*
|
||||||
|
* @param {String|Function} handler
|
||||||
|
*/
|
||||||
|
set_onfiledrop: function(handler) {
|
||||||
|
this.options.onfiledrop = handler;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle drops of files by linking to the row, if possible.
|
||||||
|
*
|
||||||
|
* HTML5 / native file drops conflict with jQueryUI draggable, which handles
|
||||||
|
* all our drop actions. So we side-step the issue by registering an additional
|
||||||
|
* drop handler on the rows parent. If the row/actions itself doesn't handle
|
||||||
|
* the drop, it should bubble and get handled here.
|
||||||
|
*/
|
||||||
|
handle_drop: function(event, target) {
|
||||||
|
// Check to see if we can handle the link
|
||||||
|
// First, find the UID
|
||||||
|
var row = this.controller._getIndexEntry($j(target).index());
|
||||||
|
if(!row || !row.uid)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var uid = row.uid;
|
||||||
|
|
||||||
|
// Get the file information
|
||||||
|
var files = [];
|
||||||
|
if(event.originalEvent && event.originalEvent.dataTransfer &&
|
||||||
|
event.originalEvent.dataTransfer.files && event.originalEvent.dataTransfer.files.length > 0)
|
||||||
|
{
|
||||||
|
files = event.originalEvent.dataTransfer.files;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exectute the custom handler code
|
||||||
|
if (this.options.onfiledrop && !this.options.onfiledrop.call(this, uid, files))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
// Link the file to the row
|
||||||
|
// just use a link widget, it's all already done
|
||||||
|
var split = uid.split('::');
|
||||||
|
var link_value = {
|
||||||
|
to_app: split.shift(),
|
||||||
|
to_id: split.join('::')
|
||||||
|
}
|
||||||
|
// Create widget and mangle to our needs
|
||||||
|
var link = et2_createWidget("link-to", {value: link_value}, this);
|
||||||
|
link.loadingFinished();
|
||||||
|
link.file_upload.set_drop_target(false);
|
||||||
|
|
||||||
|
if(row.row.tr)
|
||||||
|
{
|
||||||
|
// Ignore most of the UI, just use the status indicators
|
||||||
|
var status = $j(document.createElement("div"))
|
||||||
|
.addClass('et2_link_to')
|
||||||
|
.height(row.row.tr.height())
|
||||||
|
.width(row.row.tr.width())
|
||||||
|
.position({my: "left top", at: "left top", of: row.row.tr})
|
||||||
|
.append(link.status_span)
|
||||||
|
.append(link.file_upload.progress)
|
||||||
|
.appendTo(row.row.tr);
|
||||||
|
|
||||||
|
// Bind to link event so we can remove when done
|
||||||
|
link.div.on('link.et2_link_to', function(e, linked) {
|
||||||
|
if(!linked)
|
||||||
|
{
|
||||||
|
$j("li.success", link.file_upload.progress)
|
||||||
|
.removeClass('success').addClass('validation_error');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Update row
|
||||||
|
link._parent.refresh(uid,'edit');
|
||||||
|
}
|
||||||
|
// Fade out nicely
|
||||||
|
status.delay(linked ? 1 : 2000)
|
||||||
|
.fadeOut(500, function() {
|
||||||
|
link.free();
|
||||||
|
status.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload and link - this triggers the upload, which triggers the link, which triggers the cleanup and refresh
|
||||||
|
link.file_upload.set_value(files);
|
||||||
|
},
|
||||||
|
|
||||||
getDOMNode: function(_sender) {
|
getDOMNode: function(_sender) {
|
||||||
if (_sender == this)
|
if (_sender == this)
|
||||||
|
@ -214,7 +214,7 @@ var et2_link_to = et2_inputWidget.extend(
|
|||||||
id: this.id + '_file',
|
id: this.id + '_file',
|
||||||
|
|
||||||
// Make the whole template a drop target
|
// Make the whole template a drop target
|
||||||
drop_target: this.getRoot(),
|
drop_target: this.getInstanceManager().DOMContainer.getAttribute("id"),
|
||||||
|
|
||||||
// Change to this tab when they drop
|
// Change to this tab when they drop
|
||||||
onStart: function(event, file_count) {
|
onStart: function(event, file_count) {
|
||||||
@ -316,6 +316,7 @@ var et2_link_to = et2_inputWidget.extend(
|
|||||||
if(success) {
|
if(success) {
|
||||||
this.comment.hide();
|
this.comment.hide();
|
||||||
this.link_button.hide().attr("disabled", false);
|
this.link_button.hide().attr("disabled", false);
|
||||||
|
this.status_span.removeClass("error").addClass("success");
|
||||||
this.status_span.fadeIn().delay(1000).fadeOut();
|
this.status_span.fadeIn().delay(1000).fadeOut();
|
||||||
delete this.options.value.app;
|
delete this.options.value.app;
|
||||||
delete this.options.value.id;
|
delete this.options.value.id;
|
||||||
@ -335,6 +336,12 @@ var et2_link_to = et2_inputWidget.extend(
|
|||||||
this, et2_link_list
|
this, et2_link_list
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.status_span.removeClass("success").addClass("error")
|
||||||
|
.fadeIn();
|
||||||
|
}
|
||||||
|
this.div.trigger('link.et2_link_to',success);
|
||||||
},
|
},
|
||||||
|
|
||||||
set_no_files: function(no_files)
|
set_no_files: function(no_files)
|
||||||
|
@ -537,12 +537,17 @@ div.et2_link_entry input.ui-autocomplete-input {
|
|||||||
.et2_link_to span.status {
|
.et2_link_to span.status {
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: 3px center;
|
background-position: 3px center;
|
||||||
background-image:url(images/tick.png);
|
|
||||||
width: 22px;
|
width: 22px;
|
||||||
height: 22px;
|
height: 22px;
|
||||||
display: block;
|
display: block;
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
.et2_link_to span.status.success {
|
||||||
|
background-image:url(images/tick.png);
|
||||||
|
}
|
||||||
|
.et2_link_to span.status.error {
|
||||||
|
background-image:url(images/error.png);
|
||||||
|
}
|
||||||
.et2_link_to .progress {
|
.et2_link_to .progress {
|
||||||
max-height: 12em;
|
max-height: 12em;
|
||||||
}
|
}
|
||||||
@ -550,6 +555,9 @@ div.et2_link_entry input.ui-autocomplete-input {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
margin-right: -20px; /* Leave room for remove icon */
|
margin-right: -20px; /* Leave room for remove icon */
|
||||||
}
|
}
|
||||||
|
.et2_link_to .progress li {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
.et2_link_to .progress li.success span.ui-icon-comment {
|
.et2_link_to .progress li.success span.ui-icon-comment {
|
||||||
display: none;
|
display: none;
|
||||||
float: right;
|
float: right;
|
||||||
@ -777,6 +785,7 @@ div.message.floating {
|
|||||||
border-color: #a93030;
|
border-color: #a93030;
|
||||||
background-image:url(images/error.png);
|
background-image:url(images/error.png);
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
|
padding-left: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message.success {
|
.message.success {
|
||||||
@ -786,6 +795,7 @@ div.message.floating {
|
|||||||
border-color: #9ea930;
|
border-color: #9ea930;
|
||||||
background-image:url(images/tick.png);
|
background-image:url(images/tick.png);
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
|
padding-left: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message.hint {
|
.message.hint {
|
||||||
@ -795,6 +805,7 @@ div.message.floating {
|
|||||||
color: #56729a;
|
color: #56729a;
|
||||||
background-image:url(images/hint.png);
|
background-image:url(images/hint.png);
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
|
padding-left: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -966,6 +977,14 @@ div.message.floating {
|
|||||||
|
|
||||||
/* End of hierarchy */
|
/* End of hierarchy */
|
||||||
|
|
||||||
|
/* Mangled link-to widget inside a nextmatch - used for DnD uploads */
|
||||||
|
.et2_nextmatch * .et2_link_to {
|
||||||
|
position: fixed;
|
||||||
|
left: 0px;
|
||||||
|
background: white;
|
||||||
|
border: 1px gray;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.et2_clickable {
|
.et2_clickable {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
Loading…
Reference in New Issue
Block a user