mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2026-09-21 08:50:05 +00:00
Improve support for drag and drop
Documentation TBD
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
/*\
|
||||
title: $:/core/modules/widgets/droppable.js
|
||||
type: application/javascript
|
||||
module-type: widget
|
||||
|
||||
Droppable widget
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
var DroppableWidget = function(parseTreeNode,options) {
|
||||
this.initialise(parseTreeNode,options);
|
||||
};
|
||||
|
||||
/*
|
||||
Inherit from the base widget class
|
||||
*/
|
||||
DroppableWidget.prototype = new Widget();
|
||||
|
||||
/*
|
||||
Render this widget into the DOM
|
||||
*/
|
||||
DroppableWidget.prototype.render = function(parent,nextSibling) {
|
||||
var self = this;
|
||||
// Remember parent
|
||||
this.parentDomNode = parent;
|
||||
// Compute attributes and execute state
|
||||
this.computeAttributes();
|
||||
this.execute();
|
||||
// Create element
|
||||
var domNode = this.document.createElement("div");
|
||||
domNode.className = "tc-droppable";
|
||||
// Add event handlers
|
||||
$tw.utils.addEventListeners(domNode,[
|
||||
{name: "dragenter", handlerObject: this, handlerMethod: "handleDragEnterEvent"},
|
||||
{name: "dragover", handlerObject: this, handlerMethod: "handleDragOverEvent"},
|
||||
{name: "dragleave", handlerObject: this, handlerMethod: "handleDragLeaveEvent"},
|
||||
{name: "drop", handlerObject: this, handlerMethod: "handleDropEvent"}
|
||||
]);
|
||||
// Insert element
|
||||
parent.insertBefore(domNode,nextSibling);
|
||||
this.renderChildren(domNode,null);
|
||||
this.domNodes.push(domNode);
|
||||
// Stack of outstanding enter/leave events
|
||||
this.currentlyEntered = [];
|
||||
};
|
||||
|
||||
DroppableWidget.prototype.enterDrag = function(event) {
|
||||
if(this.currentlyEntered.indexOf(event.target) === -1) {
|
||||
this.currentlyEntered.push(event.target);
|
||||
}
|
||||
// If we're entering for the first time we need to apply highlighting
|
||||
$tw.utils.addClass(this.domNodes[0],"tc-dragover");
|
||||
};
|
||||
|
||||
DroppableWidget.prototype.leaveDrag = function(event) {
|
||||
var pos = this.currentlyEntered.indexOf(event.target);
|
||||
if(pos !== -1) {
|
||||
this.currentlyEntered.splice(pos,1);
|
||||
}
|
||||
// Remove highlighting if we're leaving externally. The hacky second condition is to resolve a problem with Firefox whereby there is an erroneous dragenter event if the node being dragged is within the dropzone
|
||||
if(this.currentlyEntered.length === 0 || (this.currentlyEntered.length === 1 && this.currentlyEntered[0] === $tw.dragInProgress)) {
|
||||
this.currentlyEntered = [];
|
||||
$tw.utils.removeClass(this.domNodes[0],"tc-dragover");
|
||||
}
|
||||
};
|
||||
|
||||
DroppableWidget.prototype.handleDragEnterEvent = function(event) {
|
||||
this.enterDrag(event);
|
||||
// Tell the browser that we're ready to handle the drop
|
||||
event.preventDefault();
|
||||
// Tell the browser not to ripple the drag up to any parent drop handlers
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
||||
DroppableWidget.prototype.handleDragOverEvent = function(event) {
|
||||
// Check for being over a TEXTAREA or INPUT
|
||||
if(["TEXTAREA","INPUT"].indexOf(event.target.tagName) !== -1) {
|
||||
return false;
|
||||
}
|
||||
// Tell the browser that we're still interested in the drop
|
||||
event.preventDefault();
|
||||
event.dataTransfer.dropEffect = "copy"; // Explicitly show this is a copy
|
||||
};
|
||||
|
||||
DroppableWidget.prototype.handleDragLeaveEvent = function(event) {
|
||||
this.leaveDrag(event);
|
||||
};
|
||||
|
||||
DroppableWidget.prototype.handleDropEvent = function(event) {
|
||||
var self = this;
|
||||
this.leaveDrag(event);
|
||||
// Check for being over a TEXTAREA or INPUT
|
||||
if(["TEXTAREA","INPUT"].indexOf(event.target.tagName) !== -1) {
|
||||
return false;
|
||||
}
|
||||
var dataTransfer = event.dataTransfer;
|
||||
// Remove highlighting
|
||||
$tw.utils.removeClass(this.domNodes[0],"tc-dragover");
|
||||
// Try to import the various data types we understand
|
||||
$tw.utils.importDataTransfer(dataTransfer,null,function(fieldsArray) {
|
||||
fieldsArray.forEach(function(fields) {
|
||||
if(fields.title) {
|
||||
self.performActions(fields.title,event);
|
||||
}
|
||||
});
|
||||
});
|
||||
// Tell the browser that we handled the drop
|
||||
event.preventDefault();
|
||||
// Stop the drop ripple up to any parent handlers
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
||||
DroppableWidget.prototype.performActions = function(title,event) {
|
||||
if(this.dropzoneActions) {
|
||||
this.invokeActionString(this.dropzoneActions,this,event,{actionTiddler: title});
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Compute the internal state of the widget
|
||||
*/
|
||||
DroppableWidget.prototype.execute = function() {
|
||||
this.dropzoneActions = this.getAttribute("actions");
|
||||
// Make child widgets
|
||||
this.makeChildWidgets();
|
||||
};
|
||||
|
||||
/*
|
||||
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
|
||||
*/
|
||||
DroppableWidget.prototype.refresh = function(changedTiddlers) {
|
||||
return this.refreshChildren(changedTiddlers);
|
||||
};
|
||||
|
||||
exports.droppable = DroppableWidget;
|
||||
|
||||
})();
|
||||
Reference in New Issue
Block a user