Skip to content
This repository was archived by the owner on Sep 6, 2021. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
5f3c992
Add drag and drop to move items in FileTreeView
boopeshmahendran Jul 10, 2017
88e18f9
Add support for moving items to root directory
boopeshmahendran Jul 11, 2017
b856622
Check item dropped onto itself or parent directory
boopeshmahendran Jul 19, 2017
a98af02
Add support for moving items by dropping on files
boopeshmahendran Jul 19, 2017
dc98af6
Create dragItem action
boopeshmahendran Jul 21, 2017
113d25f
Close directory on drag
boopeshmahendran Jul 21, 2017
4c5bfc2
Open directory on Drop
boopeshmahendran Jul 21, 2017
b0f99b4
Open directory on drag over
boopeshmahendran Jul 21, 2017
4699ebf
Address review comments
boopeshmahendran Jul 21, 2017
4d187dc
Add tests for moveItem in FileTreeViewModel
boopeshmahendran Aug 1, 2017
9430b1b
Fix style issues on drag
boopeshmahendran Aug 17, 2017
9ab2c4a
Make styles fast
boopeshmahendran Aug 23, 2017
8651108
Change fileindex to update the moved entry
boopeshmahendran Aug 23, 2017
0954350
Fix lint mistakes
boopeshmahendran Aug 23, 2017
4146637
Set drag image as item name
boopeshmahendran Aug 27, 2017
5f9ebe4
Check if directory is open before opening directory on drop
boopeshmahendran Aug 27, 2017
b7df261
Refactor code
boopeshmahendran Aug 27, 2017
b0eee1b
Check if item is dropped onto itself or parent directory
boopeshmahendran Aug 27, 2017
d148980
Move selected flag when item is moved
boopeshmahendran Aug 27, 2017
5f8f7e7
Merge branch 'master' into dragAndDrop
boopeshmahendran Sep 6, 2017
b1f00df
Merge branch 'master' into dragAndDrop
boopeshmahendran Apr 9, 2018
45305cd
Use filter instead of forEach
boopeshmahendran Apr 9, 2018
be7a935
Change implementation to reuse rename workflow
boopeshmahendran Apr 9, 2018
0ca8bdf
Fix tests
boopeshmahendran Apr 9, 2018
09efcdb
Add docs and comments
boopeshmahendran Apr 9, 2018
e56830b
Address review comments
boopeshmahendran Apr 10, 2018
f416c65
Add Error handling
boopeshmahendran Apr 16, 2018
ed84e49
Make directory open and adding new item independent
boopeshmahendran Apr 18, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 56 additions & 7 deletions src/project/FileTreeView.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ define(function (require, exports, module) {
ReactDOM = require("thirdparty/react-dom"),
Classnames = require("thirdparty/classnames"),
Immutable = require("thirdparty/immutable"),
Menus = require("command/Menus"),
_ = require("thirdparty/lodash"),
FileUtils = require("file/FileUtils"),
LanguageManager = require("language/LanguageManager"),
Expand Down Expand Up @@ -196,6 +197,46 @@ define(function (require, exports, module) {
}
};

/**
* This is a mixin that provides drag and drop move function.
*/
var dragAndDrop = {
handleDrag: function(e) {
// Pass the dragged item path.
e.dataTransfer.setData("text", JSON.stringify({
path: this.myPath()
}));

// Close open menus on drag and clear the context, but only if there's a menu open.
if ($(".dropdown.open").length > 0) {
Menus.closeAll();
this.props.actions.setContext(null);
}

e.stopPropagation();
},
handleDrop: function(e) {
var data = JSON.parse(e.dataTransfer.getData("text"));

this.props.actions.moveItem(data.path, this.myPath());
this.props.actions.setDraggedOver(null);

e.stopPropagation();
},

/**
* Allow the drop
*/
handleDragOver: function(e) {
this.props.actions.setDraggedOver(this.myPath());
e.preventDefault();
},

handleDragLeave: function(e) {
this.props.actions.setDraggedOver(null);
}
};

/**
* @private
*
Expand Down Expand Up @@ -263,9 +304,9 @@ define(function (require, exports, module) {
}
// Return true only for mouse down in rename mode.
if (this.props.entry.get("rename")) {
e.preventDefault(); // Disable drag and drop when renaming
return;
}
e.preventDefault();
}
};

Expand Down Expand Up @@ -363,7 +404,7 @@ define(function (require, exports, module) {
* * forceRender: causes the component to run render
*/
var fileNode = React.createFactory(React.createClass({
mixins: [contextSettable, pathComputer, extendable],
mixins: [contextSettable, pathComputer, extendable, dragAndDrop],

/**
* Ensures that we always have a state object.
Expand Down Expand Up @@ -499,7 +540,9 @@ define(function (require, exports, module) {
className: this.getClasses("jstree-leaf"),
onClick: this.handleClick,
onMouseDown: this.handleMouseDown,
onDoubleClick: this.handleDoubleClick
onDoubleClick: this.handleDoubleClick,
draggable: true,
onDragStart: this.handleDrag
},
DOM.ins({
className: "jstree-icon"
Expand Down Expand Up @@ -639,7 +682,7 @@ define(function (require, exports, module) {
* * forceRender: causes the component to run render
*/
directoryNode = React.createFactory(React.createClass({
mixins: [contextSettable, pathComputer, extendable],
mixins: [contextSettable, pathComputer, extendable, dragAndDrop],

/**
* We need to update this component if the sort order changes or our entry object
Expand Down Expand Up @@ -735,14 +778,17 @@ define(function (require, exports, module) {

var directoryClasses = cx({
'jstree-clicked sidebar-selection': entry.get("selected"),
'context-node': entry.get("context")
'context-node': entry.get("context"),
'jstree-draggedOver': entry.get("draggedOver")
});

var liArgs = [
{
className: this.getClasses("jstree-" + nodeClass),
onClick: this.handleClick,
onMouseDown: this.handleMouseDown
onMouseDown: this.handleMouseDown,
draggable: true,
onDragStart: this.handleDrag
},
_createAlignedIns(this.props.depth)
];
Expand All @@ -761,7 +807,10 @@ define(function (require, exports, module) {
// Need to flatten the arguments because getIcons returns an array
var aArgs = _.flatten([{
href: "#",
className: directoryClasses
className: directoryClasses,
onDrop: this.handleDrop,
onDragOver: this.handleDragOver,
onDragLeave: this.handleDragLeave
}, thickness, this.getIcons(), this.props.name]);
nameDisplay = DOM.a.apply(DOM.a, aArgs);
}
Expand Down
34 changes: 34 additions & 0 deletions src/project/FileTreeViewModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,40 @@ define(function (require, exports, module) {
this._commit(treeData);
};

/**
* Removes the item from the oldPath and adds the item in the newPath
*
* @param {string} oldPath old path of the item
* @param {string} newPath new path of the item
*/
FileTreeViewModel.prototype.moveItem = function(oldPath, newPath) {
var treeData = this._treeData,
oldObjectPath = _filePathToObjectPath(treeData, oldPath),
newObjectPath = _filePathToObjectPath(treeData, FileUtils.getParentPath(newPath)),
itemName = _.last(oldObjectPath),
element;

// Back up to the parent directory
oldObjectPath.pop();

// Remove the oldPath
treeData = treeData.updateIn(oldObjectPath, function (directory) {
element = directory.get(itemName);
directory = directory.delete(itemName);
return directory;
});

// Add the newPath
if (treeData.getIn(newObjectPath).get("children")) {
newObjectPath.push("children");
newObjectPath.push(itemName);
treeData = _setIn(treeData, newObjectPath, element);
}


this._commit(treeData);
};

/**
* @private
*
Expand Down
43 changes: 42 additions & 1 deletion src/project/ProjectManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ define(function (require, exports, module) {
ERR_TYPE_LOADING_PROJECT_NATIVE = 6,
ERR_TYPE_MAX_FILES = 7,
ERR_TYPE_OPEN_DIALOG = 8,
ERR_TYPE_INVALID_FILENAME = 9;
ERR_TYPE_INVALID_FILENAME = 9,
ERR_TYPE_MOVE = 10;

/**
* @private
Expand Down Expand Up @@ -293,6 +294,13 @@ define(function (require, exports, module) {
this.model.restoreContext();
};

/**
* See `ProjectModel.setDraggedOver`
*/
ActionCreator.prototype.setDraggedOver = function (path) {
this.model.setDraggedOver(path);
};

/**
* See `ProjectModel.startRename`
*/
Expand Down Expand Up @@ -368,6 +376,34 @@ define(function (require, exports, module) {
_saveTreeState();
};

/**
* Moves the item in the oldPath to the newDirectory directory
*
* See `ProjectModel.moveItem`
*/
ActionCreator.prototype.moveItem = function(oldPath, newDirectory) {
var self = this;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to set this to variable here (for now at least)


// Remove selected marker
self.setSelected(null);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As of now, I just remove the selected flag if any of the items are moved. Need feedback on the correct way to handle it.


this.model.moveItem(oldPath, newDirectory)
.fail(function(errorInfo) {
// Need to do display the error message on the next event loop turn
// because some errors can come up synchronously and then the dialog
// is not displayed.
window.setTimeout(function () {
switch (errorInfo.type) {
// TODO: handle Errors
default:
console.log(errorInfo.type);
_showErrorDialog(ERR_TYPE_MOVE, errorInfo.isFolder, Strings.FILE_EXISTS_ERR, errorInfo.fullPath);
break;
}
}, 10);
});
};

/**
* See `ProjectModel.refresh`
*/
Expand Down Expand Up @@ -604,6 +640,11 @@ define(function (require, exports, module) {
title = StringUtils.format(Strings.INVALID_FILENAME_TITLE, isFolder ? Strings.DIRECTORY_NAME : Strings.FILENAME);
message = StringUtils.format(Strings.INVALID_FILENAME_MESSAGE, isFolder ? Strings.DIRECTORY_NAMES_LEDE : Strings.FILENAMES_LEDE, error);
break;
case ERR_TYPE_MOVE:
// TODO: Change it to handle move errors
title = StringUtils.format(Strings.GENERIC_ERROR, titleType);
message = StringUtils.format(Strings.GENERIC_ERROR, path);
break;
}

if (title && message) {
Expand Down
58 changes: 58 additions & 0 deletions src/project/ProjectModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,20 @@ define(function (require, exports, module) {
}
};

/**
* Moves the draggedOver flag
*
* @param {string} path full path to the folder over which dragging is done
*/
ProjectModel.prototype.setDraggedOver = function(path) {
var oldDraggedOverPath = this.makeProjectRelativeIfPossible(this._selections.draggedOver),
newDraggedOverPath = this.makeProjectRelativeIfPossible(path);

this._selections.draggedOver = path;

this._viewModel.moveMarker("draggedOver", oldDraggedOverPath, newDraggedOverPath);
};

/**
* Gets the currently selected file or directory.
*
Expand Down Expand Up @@ -1239,6 +1253,50 @@ define(function (require, exports, module) {
this._viewModel.closeSubtree(this.makeProjectRelativeIfPossible(path));
};

/**
* Moves the item in oldPath to the newDirectory directory
* @param {string} oldPath old path of the item
* @param {string} newDirectory path of the directory to move the item
* @return {$.Promise} promise resolved when the item is moved
*/
ProjectModel.prototype.moveItem = function(oldPath, newDirectory) {
var self = this,
d = new $.Deferred(),
fileName = FileUtils.getBaseName(oldPath),
fullNewPath = newDirectory + fileName;

// Add trailing slash if directory is moved
if (!_pathIsFile(oldPath)) {
fullNewPath = _ensureTrailingSlash(fullNewPath);
}

// Reusing the _renameItem for moving item
this._renameItem(oldPath, fullNewPath, fileName, !_pathIsFile(fullNewPath)).then(function () {
var newDirectoryRelative = self.makeProjectRelativeIfPossible(newDirectory),
needsLoading = !self._viewModel.isPathLoaded(newDirectoryRelative);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: some extra spaces after needsLoading

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe save the reference to var viewModel = self._viewModel to make the calls a bit less terse.


// If directory view not loaded, load it and then update the view
if (needsLoading) {
self._getDirectoryContents(newDirectory).then(function(contents) {
self._viewModel.setDirectoryContents(newDirectoryRelative, contents);
self._viewModel.moveItem(self.makeProjectRelativeIfPossible(oldPath), self.makeProjectRelativeIfPossible(fullNewPath));
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe make these variables too self.makeProjectRelativeIfPossible(oldPath) self.makeProjectRelativeIfPossible(fullNewPath) as the long lines are pretty hard to distinquish.

});
} else {
self._viewModel.moveItem(self.makeProjectRelativeIfPossible(oldPath), self.makeProjectRelativeIfPossible(fullNewPath));
}
d.resolve();
}).fail(function (errorType) {
var errorInfo = {
type: errorType,
isFolder: !_pathIsFile(fullNewPath),
fullPath: fullNewPath
};
d.reject(errorInfo);
});

return d.promise();
};

/**
* Toggle the open state of subdirectories.
* @param {!string} path parent directory
Expand Down
6 changes: 5 additions & 1 deletion src/styles/jsTreeTheme.less
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
.jstree > ul > li { margin-left:0; }
.jstree-rtl > ul > li { margin-right:0; }
.jstree ins { display:inline-block; text-decoration:none; width:18px; height:18px; margin:0 0 0 0; padding:0; }
.jstree a:focus { outline: none; }
.jstree a:focus { outline: none; box-shadow: none; }
.jstree a > ins { height:16px; width:16px; }
.jstree a > .jstree-icon { margin-right:3px; }
.jstree-rtl a > .jstree-icon { margin-left:3px; margin-right:0; }
Expand All @@ -51,6 +51,10 @@ li.jstree-closed > ul { display:none; }
text-decoration: none;
}

.jstree-draggedOver {
background-color: @bc-sidebar-selection;
}

@jstree-icon-backindent: 12px;

/* Make the links in the JS tree the width of the container
Expand Down