mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2025-02-17 11:30:02 +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:
parent
df59269b0d
commit
e39d246317
@ -28,6 +28,7 @@ var ImportWidget = function(renderer) {
|
|||||||
ImportWidget.prototype.generate = function() {
|
ImportWidget.prototype.generate = function() {
|
||||||
// Get the parameters from the attributes
|
// Get the parameters from the attributes
|
||||||
this.browse = this.renderer.getAttribute("browse","yes");
|
this.browse = this.renderer.getAttribute("browse","yes");
|
||||||
|
this.mutate = this.renderer.getAttribute("mutate","yes");
|
||||||
this["class"] = this.renderer.getAttribute("class");
|
this["class"] = this.renderer.getAttribute("class");
|
||||||
// Compute classes
|
// Compute classes
|
||||||
var classes = ["tw-import"];
|
var classes = ["tw-import"];
|
||||||
@ -69,52 +70,76 @@ ImportWidget.prototype.handleChangeEvent = function(event) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
ImportWidget.prototype.handleDragEnterEvent = 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) {
|
ImportWidget.prototype.handleDragOverEvent = function(event) {
|
||||||
event.stopPropagation();
|
// Tell the browser that we're still interested in the drop
|
||||||
event.preventDefault();
|
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) {
|
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) {
|
ImportWidget.prototype.handleDropEvent = function(event) {
|
||||||
this.renderer.domNode.classList.remove("tw-dragover");
|
var dataTransfer = event.dataTransfer;
|
||||||
event.stopPropagation();
|
// 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();
|
event.preventDefault();
|
||||||
this.importFiles(event.dataTransfer.files);
|
// Stop the drop ripple up to any parent handlers
|
||||||
return false;
|
event.stopPropagation();
|
||||||
};
|
};
|
||||||
|
|
||||||
ImportWidget.prototype.handlePasteEvent = function(event) {
|
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,
|
var self = this,
|
||||||
items = event.clipboardData.items;
|
items = event.clipboardData.items;
|
||||||
|
// Enumerate the clipboard items
|
||||||
for(var t = 0; t<items.length; t++) {
|
for(var t = 0; t<items.length; t++) {
|
||||||
var item = items[t];
|
var item = items[t];
|
||||||
if(item.kind === "file") {
|
if(item.kind === "file") {
|
||||||
|
// Import any files
|
||||||
var file = item.getAsFile();
|
var file = item.getAsFile();
|
||||||
this.importFiles([file]);
|
this.importFiles([file]);
|
||||||
} else if(item.kind === "string") {
|
} else if(item.kind === "string") {
|
||||||
|
// Create tiddlers from string items
|
||||||
item.getAsString(function(str) {
|
item.getAsString(function(str) {
|
||||||
var fields = {
|
var fields = {
|
||||||
title: self.generateTitle("Untitled"),
|
title: self.generateTitle("Untitled"),
|
||||||
text: str
|
text: str
|
||||||
}
|
};
|
||||||
self.renderer.renderTree.wiki.addTiddler(new $tw.Tiddler(fields));
|
self.renderer.renderTree.wiki.addTiddler(new $tw.Tiddler(fields));
|
||||||
self.openTiddler(fields.title);
|
self.openTiddler(fields.title);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Tell the browser that we've handled the paste
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.preventDefault();
|
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) {
|
ImportWidget.prototype.importFiles = function(files) {
|
||||||
var self = this,
|
var self = this,
|
||||||
importFile = function(file) {
|
importFile = function(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) {
|
if(this.hover) {
|
||||||
events.push({name: "mouseover", handlerObject: this, handlerMethod: "handleMouseOverOrOutEvent"});
|
events.push({name: "mouseover", handlerObject: this, handlerMethod: "handleMouseOverOrOutEvent"});
|
||||||
events.push({name: "mouseout", 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)));
|
wikiLinkText = wikiLinkText.replace("$uri_doubleencoded$",encodeURIComponent(encodeURIComponent(this.to)));
|
||||||
this.attributes.href = wikiLinkText;
|
this.attributes.href = wikiLinkText;
|
||||||
}
|
}
|
||||||
|
this.events = events;
|
||||||
} else {
|
} else {
|
||||||
this.tag = "span";
|
this.tag = "span";
|
||||||
}
|
}
|
||||||
this.children = this.renderer.renderTree.createRenderers(this.renderer.renderContext,this.renderer.parseTreeNode.children);
|
this.children = this.renderer.renderTree.createRenderers(this.renderer.renderContext,this.renderer.parseTreeNode.children);
|
||||||
this.events = events;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
LinkWidget.prototype.handleClickEvent = function(event) {
|
LinkWidget.prototype.handleClickEvent = function(event) {
|
||||||
@ -113,6 +117,44 @@ LinkWidget.prototype.handleMouseOverOrOutEvent = function(event) {
|
|||||||
return false;
|
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) {
|
LinkWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
|
||||||
// Set the class for missing tiddlers
|
// Set the class for missing tiddlers
|
||||||
if(this.targetTitle && changedTiddlers[this.targetTitle]) {
|
if(this.targetTitle && changedTiddlers[this.targetTitle]) {
|
||||||
|
@ -387,6 +387,22 @@ exports.getTiddlersWithTag = function(tag) {
|
|||||||
return titles;
|
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:
|
Get a tiddlers content as a JavaScript object. How this is done depends on the type of the tiddler:
|
||||||
|
|
||||||
|
@ -94,6 +94,20 @@ a.tw-tiddlylink-missing {
|
|||||||
font-style: italic;
|
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 {
|
.btn-invisible {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@ -376,13 +390,30 @@ embed {
|
|||||||
height: 20em;
|
height: 20em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tw-dragover .tw-drag-target {
|
.tw-import {
|
||||||
display: block;
|
position: relative;
|
||||||
background: red;
|
|
||||||
position: absolute;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tw-drag-target {
|
.tw-drop-zone-fullscreen.tw-dragover:before {
|
||||||
display: none;
|
z-index: 10000;
|
||||||
position: absolute;
|
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";
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,11 @@ title: $:/templates/ControlPanel
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Import: <$import/>
|
Import: <$import mutate="sidebar importer" class="tw-drop-zone">
|
||||||
|
|
||||||
|
Drop files here
|
||||||
|
|
||||||
|
</$import>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -4,11 +4,7 @@ title: $:/templates/PageTemplate
|
|||||||
<$navigator story="$:/StoryList" history="$:/HistoryList">
|
<$navigator story="$:/StoryList" history="$:/HistoryList">
|
||||||
|
|
||||||
|
|
||||||
<$import browse="no">
|
<$import browse="no" mutate="global importer" class="tw-drop-zone-fullscreen">
|
||||||
|
|
||||||
<div class="tw-drag-target">
|
|
||||||
Drop files here to import them as tiddlers
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- The sidebar header -->
|
<!-- The sidebar header -->
|
||||||
<header class="sidebar-header">
|
<header class="sidebar-header">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user