1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-11-23 18:17:20 +00:00

Added basic support for drag and drop of tiddlers

Links are now draggable. Dragging outside the browser will drop the
wiki text of the tiddler. Dragging to another TW5 browser window will
drop the entire tiddler, including fields.
This commit is contained in:
Jeremy Ruston 2013-04-08 18:47:46 +01:00
parent df59269b0d
commit e39d246317
6 changed files with 174 additions and 28 deletions

View File

@ -28,6 +28,7 @@ var ImportWidget = function(renderer) {
ImportWidget.prototype.generate = function() {
// Get the parameters from the attributes
this.browse = this.renderer.getAttribute("browse","yes");
this.mutate = this.renderer.getAttribute("mutate","yes");
this["class"] = this.renderer.getAttribute("class");
// Compute classes
var classes = ["tw-import"];
@ -69,52 +70,76 @@ ImportWidget.prototype.handleChangeEvent = function(event) {
};
ImportWidget.prototype.handleDragEnterEvent = function(event) {
this.renderer.domNode.classList.add("tw-dragover");
// We count enter/leave events
this.dragEnterCount = (this.dragEnterCount || 0) + 1;
// If we're entering for the first time we need to apply highlighting
if(this.dragEnterCount === 1) {
$tw.utils.addClass(this.renderer.domNode,"tw-dragover");
}
// 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();
};
ImportWidget.prototype.handleDragOverEvent = function(event) {
event.stopPropagation();
// Tell the browser that we're still interested in the drop
event.preventDefault();
event.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
event.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy
};
ImportWidget.prototype.handleDragLeaveEvent = function(event) {
this.renderer.domNode.classList.remove("tw-dragover");
// Reduce the enter count
this.dragEnterCount = (this.dragEnterCount || 0) - 1;
// Remove highlighting if we're leaving externally
if(this.dragEnterCount <= 0) {
$tw.utils.removeClass(this.renderer.domNode,"tw-dragover");
}
};
ImportWidget.prototype.handleDropEvent = function(event) {
this.renderer.domNode.classList.remove("tw-dragover");
event.stopPropagation();
var dataTransfer = event.dataTransfer;
// Reset the enter count
this.dragEnterCount = 0;
// Remove highlighting
$tw.utils.removeClass(this.renderer.domNode,"tw-dragover");
// Try to import the various data types we understand
this.importData(dataTransfer);
// Import any files in the drop
this.importFiles(dataTransfer.files);
// Tell the browser that we handled the drop
event.preventDefault();
this.importFiles(event.dataTransfer.files);
return false;
// Stop the drop ripple up to any parent handlers
event.stopPropagation();
};
ImportWidget.prototype.handlePasteEvent = function(event) {
if(["TEXTAREA","INPUT"].indexOf(event.srcElement.tagName) == -1) {
// Let the browser handle it if we're in a textarea or input box
if(["TEXTAREA","INPUT"].indexOf(event.target.tagName) == -1) {
var self = this,
items = event.clipboardData.items;
// Enumerate the clipboard items
for(var t = 0; t<items.length; t++) {
var item = items[t];
if(item.kind === "file") {
// Import any files
var file = item.getAsFile();
this.importFiles([file]);
} else if(item.kind === "string") {
// Create tiddlers from string items
item.getAsString(function(str) {
var fields = {
title: self.generateTitle("Untitled"),
text: str
}
};
self.renderer.renderTree.wiki.addTiddler(new $tw.Tiddler(fields));
self.openTiddler(fields.title);
});
}
}
// Tell the browser that we've handled the paste
event.stopPropagation();
event.preventDefault();
return false;
} else {
return true;
}
};
@ -126,6 +151,38 @@ ImportWidget.prototype.openTiddler = function(title) {
});
};
ImportWidget.prototype.importData = function(dataTransfer) {
for(var t=0; t<this.importDataTypes.length; t++) {
var dataType = this.importDataTypes[t];
var data = dataTransfer.getData(dataType.type);
if(data !== "") {
var fields = dataType.handler(data);
if(!fields.title) {
fields.title = this.generateTitle("Untitled");
}
this.renderer.renderTree.wiki.addTiddler(new $tw.Tiddler(fields));
this.openTiddler(fields.title);
return;
}
};
};
ImportWidget.prototype.importDataTypes = [
{type: "text/vnd.tiddler", handler: function(data) {
return JSON.parse(data);
}},
{type: "text/plain", handler: function(data) {
return {
text: data
};
}},
{type: "text/uri-list", handler: function(data) {
return {
text: data
};
}}
];
ImportWidget.prototype.importFiles = function(files) {
var self = this,
importFile = function(file) {

View File

@ -56,7 +56,11 @@ LinkWidget.prototype.generate = function() {
}
}
}
var events = [{name: "click", handlerObject: this, handlerMethod: "handleClickEvent"}];
var events = [
{name: "click", handlerObject: this, handlerMethod: "handleClickEvent"},
{name: "dragstart", handlerObject: this, handlerMethod: "handleDragStartEvent"},
{name: "dragend", handlerObject: this, handlerMethod: "handleDragEndEvent"}
];
if(this.hover) {
events.push({name: "mouseover", handlerObject: this, handlerMethod: "handleMouseOverOrOutEvent"});
events.push({name: "mouseout", handlerObject: this, handlerMethod: "handleMouseOverOrOutEvent"});
@ -79,11 +83,11 @@ LinkWidget.prototype.generate = function() {
wikiLinkText = wikiLinkText.replace("$uri_doubleencoded$",encodeURIComponent(encodeURIComponent(this.to)));
this.attributes.href = wikiLinkText;
}
this.events = events;
} else {
this.tag = "span";
}
this.children = this.renderer.renderTree.createRenderers(this.renderer.renderContext,this.renderer.parseTreeNode.children);
this.events = events;
};
LinkWidget.prototype.handleClickEvent = function(event) {
@ -113,6 +117,44 @@ LinkWidget.prototype.handleMouseOverOrOutEvent = function(event) {
return false;
};
LinkWidget.prototype.handleDragStartEvent = function(event) {
if(this.to) {
// Set the dragging class on the element being dragged
$tw.utils.addClass(event.target,"tw-tiddlylink-dragging");
// Create the drag image element
this.dragImage = document.createElement("div");
this.dragImage.className = "tw-tiddler-dragger";
this.dragImage.appendChild(document.createTextNode(this.to));
document.body.appendChild(this.dragImage);
// Set the data transfer properties
var dataTransfer = event.dataTransfer;
dataTransfer.effectAllowed = "copy";
dataTransfer.setDragImage(this.dragImage,-16,-16);
dataTransfer.clearData();
dataTransfer.setData("text/vnd.tiddler",this.renderer.renderTree.wiki.getTiddlerAsJson(this.to));
dataTransfer.setData("text/plain",this.renderer.renderTree.wiki.getTiddlerText(this.to,""));
event.stopPropagation();
} else {
event.preventDefault();
}
};
LinkWidget.prototype.handleDragEndEvent = function(event) {
// Remove the dragging class on the element being dragged
$tw.utils.removeClass(event.target,"tw-tiddlylink-dragging");
// Delete the drag image element
if(this.dragImage) {
this.dragImage.parentNode.removeChild(this.dragImage);
}
};
LinkWidget.prototype.postRenderInDom = function() {
// Add the draggable attribute to links (we don't include it in the static HTML representation)
if(this.renderer.domNode.tagName === "A") {
this.renderer.domNode.setAttribute("draggable",true);
}
};
LinkWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
// Set the class for missing tiddlers
if(this.targetTitle && changedTiddlers[this.targetTitle]) {

View File

@ -387,6 +387,22 @@ exports.getTiddlersWithTag = function(tag) {
return titles;
};
/*
Retrieve a tiddler as a JSON string of the fields
*/
exports.getTiddlerAsJson = function(title) {
var tiddler = this.getTiddler(title);
if(tiddler) {
var fields = {};
$tw.utils.each(tiddler.fields,function(value,name) {
fields[name] = tiddler.getFieldString(name);
});
return JSON.stringify(fields);
} else {
return JSON.stringify({title: title});
}
};
/*
Get a tiddlers content as a JavaScript object. How this is done depends on the type of the tiddler:

View File

@ -94,6 +94,20 @@ a.tw-tiddlylink-missing {
font-style: italic;
}
.tw-tiddler-dragger {
display: inline-block;
padding: 8px 20px;
font-size: 16.9px;
font-weight: bold;
line-height: 20px;
color: #fff;
text-shadow: 0 1px 0 rgba(0, 0, 0, 1);
white-space: nowrap;
vertical-align: baseline;
background-color: #000;
<<border-radius 20px>>
}
.btn-invisible {
padding: 0;
margin: 0;
@ -376,13 +390,30 @@ embed {
height: 20em;
}
.tw-dragover .tw-drag-target {
display: block;
background: red;
position: absolute;
.tw-import {
position: relative;
}
.tw-drag-target {
display: none;
position: absolute;
.tw-drop-zone-fullscreen.tw-dragover:before {
z-index: 10000;
display: block;
position: fixed;
top: 0;
left: 0;
right: 0;
background: rgba(0,200,0,0.7);
text-align: center;
content: "Drop here";
}
.tw-drop-zone.tw-dragover:before {
z-index: 10000;
display: block;
position: absolute;
top: 0;
left: 0;
right: 0;
background: rgba(0,200,0,0.7);
text-align: center;
content: "Drop here";
}

View File

@ -4,7 +4,11 @@ title: $:/templates/ControlPanel
---
Import: <$import/>
Import: <$import mutate="sidebar importer" class="tw-drop-zone">
Drop files here
</$import>
---

View File

@ -4,11 +4,7 @@ title: $:/templates/PageTemplate
<$navigator story="$:/StoryList" history="$:/HistoryList">
<$import browse="no">
<div class="tw-drag-target">
Drop files here to import them as tiddlers
</div>
<$import browse="no" mutate="global importer" class="tw-drop-zone-fullscreen">
<!-- The sidebar header -->
<header class="sidebar-header">