Modules (188)

FileTreeView

Description

This is the view layer (template) for the file tree in the sidebar. It takes a FileTreeViewModel and renders it to the given element using Preact. User actions are signaled via an ActionCreator (in the Flux sense).

Dependencies

Variables

Private

_draggedItemPath

Type
string
    var _draggedItemPath;


    // Constants

    // Time range from first click to second click to invoke renaming.
    var CLICK_RENAME_MINIMUM  = 500,
        RIGHT_MOUSE_BUTTON    = 2,
        LEFT_MOUSE_BUTTON     = 0;

    var INDENTATION_WIDTH     = 10;
Private

_extensions

Type
Immutable.Map
    var _extensions = Immutable.Map();
Private

contextSettable

    var contextSettable = {
Private

directoryRenameInput

    var directoryRenameInput = Preact.createFactory(Preact.createClass({
        mixins: [renameBehavior],

dragAndDrop

This is a mixin that provides drag and drop move function.

    var dragAndDrop = {
        handleDrag: function(e) {
            // Disable drag when renaming
            if (this.props.entry.get("rename")) {
                e.preventDefault();
                e.stopPropagation();
                return false;
            }

            // In newer CEF versions, the drag and drop data from the event
            // (i.e. e.dataTransfer.getData) cannot be used to read data in dragOver event,
            // so store the drag and drop data in a global variable to read it in the dragOver
            // event.
            _draggedItemPath = this.myPath();

            // Pass the dragged item path.
            e.dataTransfer.setData("text", JSON.stringify({
                path: _draggedItemPath
            }));

            this.props.actions.dragItem(this.myPath());

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

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

            this.clearDragTimeout();
            e.stopPropagation();
        },

        handleDragEnd: function(e) {
            this.clearDragTimeout();
        },

        handleDragOver: function(e) {
            var data = e.dataTransfer.getData("text"),
                path;

            if (data) {
                path = JSON.parse(data).path;
            } else {
                path = _draggedItemPath;
            }

            if (path === this.myPath() || FileUtils.getParentPath(path) === this.myPath()) {
                e.preventDefault();
                e.stopPropagation();
                return;
            }
            var self = this;
            this.setDraggedOver(true);

            // Open the directory tree when item is dragged over a directory
            if (!this.dragOverTimeout) {
                this.dragOverTimeout = window.setTimeout(function() {
                    self.props.actions.setDirectoryOpen(self.myPath(), true);
                    self.dragOverTimeout = null;
                }, 800);
            }

            e.preventDefault(); // Allow the drop
            e.stopPropagation();
        },

        handleDragLeave: function(e) {
            this.setDraggedOver(false);
            this.clearDragTimeout();
        },

        clearDragTimeout: function() {
            if (this.dragOverTimeout) {
                clearTimeout(this.dragOverTimeout);
                this.dragOverTimeout = null;
            }
        },
        setDraggedOver: function(draggedOver) {
            if (this.state.draggedOver !== draggedOver) {
                this.setState({
                    draggedOver: draggedOver
                });
            }
        },

        setDragImage: function(e) {
            var div = window.document.createElement('div');
            div.textContent = this.props.name;
            div.classList.add('jstree-dragImage');
            window.document.body.appendChild(div);
            e.dataTransfer.setDragImage(div, -10, -10);
            setTimeout(function() {
                window.document.body.removeChild(div);
            }, 0);
        }
    };

extendable

Mixin for components that support the "icons" and "addClass" extension points. fileNode and directoryNode support this.

    var extendable = {
Private

fileNode

    var fileNode = Preact.createFactory(Preact.createClass({
        mixins: [contextSettable, pathComputer, extendable, dragAndDrop],
Private

fileRenameInput

    var fileRenameInput = Preact.createFactory(Preact.createClass({
        mixins: [renameBehavior],

fileSelectionBox

Displays the absolutely positioned box for the selection or context in the file tree. Its position is determined by passed-in info about the scroller in which the tree resides and the top of the selected node (as reported by the node itself).

Props:

  • selectionViewInfo: Immutable.Map with width, scrollTop, scrollLeft and offsetTop for the tree container
  • visible: should this be visible now
  • selectedClassName: class name applied to the element that is selected
    var fileSelectionBox = Preact.createFactory(Preact.createClass({
Private

fileTreeView

    var fileTreeView = Preact.createFactory(Preact.createClass({

pathComputer

Mixin that allows a component to compute the full path to its directory entry.

    var pathComputer = {

renameBehavior

This is a mixin that provides rename input behavior. It is responsible for taking keyboard input and invoking the correct action based on that input.

    var renameBehavior = {

selectionExtension

On Windows and Linux, the selection bar in the tree does not extend over the scroll bar. The selectionExtension sits on top of the scroll bar to make the selection bar appear to span the whole width of the sidebar.

Props:

  • selectionViewInfo: Immutable.Map with width, scrollTop, scrollLeft and offsetTop for the tree container
  • visible: should this be visible now
  • selectedClassName: class name applied to the element that is selected
  • className: class to be applied to the extension element
    var selectionExtension = Preact.createFactory(Preact.createClass({

Functions

Private

_addExtension

category string
Category to which the extension is being added
callback function
The extension function itself
    function _addExtension(category, callback) {
        if (!callback || typeof callback !== "function") {
            console.error("Attempt to add FileTreeView", category, "extension without a callback function");
            return;
        }
        var callbackList = _extensions.get(category);
        if (!callbackList) {
            callbackList = Immutable.List();
        }
        callbackList = callbackList.push(callback);
        _extensions = _extensions.set(category, callbackList);
    }
Private

_buildDirsFirstComparator

contents Immutable.Map
The directory's contents
Returns: function(string,string)
Comparator that sorts directories first.
    function _buildDirsFirstComparator(contents) {
        function _dirsFirstCompare(a, b) {
            var aIsFile = FileTreeViewModel.isFile(contents.get(a)),
                bIsFile = FileTreeViewModel.isFile(contents.get(b));

            if (!aIsFile && bIsFile) {
                return -1;
            } else if (aIsFile && !bIsFile) {
                return 1;
            } else {
                return FileUtils.compareFilenames(a, b);
            }
        }
        return _dirsFirstCompare;
    }
Private

_createAlignedIns

depth int
The depth of the current node.
Returns: PreactComponent
The resulting ins.
    function _createAlignedIns(depth) {
        return DOM.ins({
            className: "jstree-icon",
            style: {
                marginLeft: INDENTATION_WIDTH * depth
            }
        });
    }
Private

_createThickness

depth int
The depth of the current node.
Returns: PreactComponent
The resulting div.
    function _createThickness(depth) {
        return DOM.div({
            style: {
                display: "inline-block",
                width: INDENTATION_WIDTH * depth
            }
        });
    }
Private

_getName

fullname string
The complete name of the file (not including the rest of the path)
extension string
The file extension
Returns: string
The fullname without the extension
    function _getName(fullname, extension) {
        return extension !== "" ? fullname.substring(0, fullname.length - extension.length - 1) : fullname;
    }
Private

_measureText

text string
Text to measure
Returns: int
Width to use
    function _measureText(text) {
        var measuringElement = $("<span />", { css : { "position" : "absolute", "top" : "-200px", "left" : "-1000px", "visibility" : "hidden", "white-space": "pre" } }).appendTo("body");
        measuringElement.text("pW" + text);
        var width = measuringElement.width();
        measuringElement.remove();
        return width;
    }
Private

_sortDirectoryContents

contents Immutable.Map
the directory's contents
dirsFirst boolean
true to sort subdirectories first
Returns: Immutable.Map
sorted mapping
    function _sortDirectoryContents(contents, dirsFirst) {
        if (dirsFirst) {
            return contents.keySeq().sort(_buildDirsFirstComparator(contents));
        } else {
            return contents.keySeq().sort(FileUtils.compareFilenames);
        }
    }

    // Forward references to keep JSLint happy.
    var directoryNode, directoryContents;
Public API

addClassesProvider

See
ProjectManager.addClassesProvider
    function addClassesProvider(callback) {
        _addExtension("addClass", callback);
    }

    // Private API for testing
    exports._sortFormattedDirectory = _sortDirectoryContents;
    exports._fileNode = fileNode;
    exports._directoryNode = directoryNode;
    exports._directoryContents = directoryContents;
    exports._fileTreeView = fileTreeView;

    // Public API
    exports.addIconProvider = addIconProvider;
    exports.addClassesProvider = addClassesProvider;
    exports.render = render;
});
Public API

addIconProvider

See
ProjectManager.addIconProvider
    function addIconProvider(callback) {
        _addExtension("icons", callback);
    }
Private

isDefined

value Object
value to test
Returns: boolean
true if value is defined
    function isDefined(value) {
        return value !== undefined;
    }
Public API

render

Renders the file tree to the given element.

element DOMNode,jQuery
Element in which to render this file tree
viewModel FileTreeViewModel
the data container
projectRoot Directory
Directory object from which the fullPath of the project root is extracted
actions ActionCreator
object with methods used to communicate events that originate from the user
forceRender boolean
Run render on the entire tree (useful if an extension has new data that it needs rendered)
platform string
mac, win, linux
    function render(element, viewModel, projectRoot, actions, forceRender, platform) {
        if (!projectRoot) {
            return;
        }

        Preact.render(fileTreeView({
            treeData: viewModel.treeData,
            selectionViewInfo: viewModel.selectionViewInfo,
            sortDirectoriesFirst: viewModel.sortDirectoriesFirst,
            parentPath: projectRoot.fullPath,
            actions: actions,
            extensions: _extensions,
            platform: platform,
            forceRender: forceRender
        }),
              element);
    }