var classNames = require('classnames'); var AppActions = require('../actions/AppActions'); var Files = require('../stores/Files'); var React = require('react'); var ReactBootstrap = require('react-bootstrap'); var Glyphicon = ReactBootstrap.Glyphicon; // The DepotTree is a React tree control that expects to interact with a // server API that doesn't like to provide tree data, just lists of one // level at a time. // // This control creates it's own cache of the available depot, since our 'store' // doesn't really know much about the existing server. What this means, is that // all directories are generally expandable, even if they don't have children, // and we won't know if the dir contains anything until they are expanded. // // Properties: // - dirsOnly: when true, will not display files // var DepotTree = React.createClass({ getInitialState: function() { return { depots: [], selected: null }; }, componentDidMount: function() { Files.addDepotsListedListener(this.handleDepotsListed); Files.addDirListedListener(this.handleDirListed); AppActions.listDepots(); }, componentWillUnmount: function() { Files.removeDepotsListedListener(this.handleDepotsListed); Files.removeDirListedListener(this.handleDirListed); }, render: function() { var self = this; return (
{this.state.depots.map(function(x) { return renderItem(self, x); })}
); }, handleDepotsListed: function(depots) { depots.sort(function(x, y) { return x.name.localeCompare(y.name); }); this.setState({ depots: depots }); }, handleDirListed: function(dirPath, items) { var dir = findDir(this.state.depots, dirPath); items.forEach(function(i) { dir.insert(i) }); dir.sortChildren(); dir.loaded = true; this.setState({ depots: this.state.depots }); }, openFolder: function(dir, evt) { dir.expanded = true; if (!dir.loaded) { AppActions.listDir(dir.pathId.join('/')); } this.setState({ depots: this.state.depots }); evt.stopPropagation(); }, closeFolder: function(dir, evt) { dir.expanded = false; this.setState({ depots: this.state.depots }); evt.stopPropagation(); }, clickItem: function(dir, evt) { var cur = this.state.selected; if (cur && cur.pathId.join('/') == dir.pathId.join('/')) { // Same node dir.selected = false; this.state.selected = null; } else { if (cur) { cur.selected = false; } dir.selected = true; this.state.selected = dir; } if (this.state.selected && typeof this.props.onSelect == "function") { this.props.onSelect(this.state.selected); } this.setState({ depots: this.state.depots }); evt.stopPropagation(); } }); //---------------------------------------------------------------------------- // Local (private) helper methods //---------------------------------------------------------------------------- // Render a row of our depot tree, and all children function renderItem(view, item) { if (view.props.dirsOnly && !item.children) { return null; } var children = null; if (item.children && item.expanded) { children = item.children.map(function(child) { return renderItem(view, child); }); } var control = renderControl(view, item); var icon = renderIcon(item); var text = renderText(item); var classes = classNames({ 'depot-tree-item': true, 'selected': item.selected }); return (
{control} {icon} {text}
{children}
) } // The control depends on the 'expanded' attribute being set to 'true'. function renderControl(view, item) { if (item.children) { if (item.expanded) { return ; } else { return ; } } } function renderIcon(item) { if (item.children) { if (item.expanded) { return } else { return } } else { return } } // We *might* want to render folders in a different font than files. We'll see. function renderText(item) { return { item.name }; } function findDir(depots, dirPath) { if (dirPath.startsWith('//')) { dirPath = dirPath.substring(2); } var paths = dirPath.split('/'); return findDirRecurse(depots, paths, 0); } function findDirRecurse(items, paths, level) { var curName = paths[level]; var item = items.find(function(x) { return x.name == curName; }); // Halting condition. Item can be null, in which case we've not found it. if (level == (paths.length - 1)) { return item; } if (item && item.children) { return findDirRecurse(item.children, paths, level + 1); } return null; } module.exports = DepotTree;