Tree.js 6.6 KB
/**
 * @class SimpleTasks.view.lists.Tree
 * @extends Ext.tree.Panel
 * The task list view.  A tree that displays all of the task lists.
 */
Ext.define('SimpleTasks.view.lists.Tree', {
    extend: 'Ext.tree.Panel',
    xtype: 'listTree',
    requires: [
        'Ext.grid.plugin.CellEditing',
        'Ext.tree.plugin.TreeViewDragDrop',
        'Ext.grid.column.Action'
    ],
    title: 'Lists',
    store: 'Lists',
    hideHeaders: true,

    dockedItems: [
        {
            xtype: 'toolbar',
            dock: 'bottom',
            items: [
                {
                    iconCls: 'tasks-new-list',
                    tooltip: 'New List'
                },
                {
                    iconCls: 'tasks-delete-list',
                    id: 'delete-list-btn',
                    tooltip: 'Delete List'
                },
                {
                    iconCls: 'tasks-new-folder',
                    tooltip: 'New Folder'
                },
                {
                    iconCls: 'tasks-delete-folder',
                    id: 'delete-folder-btn',
                    tooltip: 'Delete Folder'
                }
            ]
        }
    ],

    viewConfig: {
            plugins: {
                ptype: 'tasksdragdrop',
                dragText: 'Drag to reorder',
                ddGroup: 'task'
            }
    },

    initComponent: function() {
        var me = this;
            
        /**
         * This Tree Panel's cell editing plugin
         * @property cellEditingPlugin
         * @type Ext.grid.plugin.CellEditing
         */
        me.plugins = [me.cellEditingPlugin = Ext.create('Ext.grid.plugin.CellEditing')];

        me.columns = [
            {
                xtype: 'treecolumn',
                dataIndex: 'name',
                flex: 1,
                editor: {
                    xtype: 'textfield',
                    selectOnFocus: true,
                    validator: function(value){
                        value = Ext.String.trim(value);
                        return value.length < 1 ? this.blankText : true;
                    }
                },
                renderer: Ext.bind(me.renderName, me)
            },
            {
                xtype: 'actioncolumn',
                width: 24,
                icon: 'resources/images/delete.png',
                iconCls: 'x-hidden',
                tooltip: 'Delete',
                handler: Ext.bind(me.handleDeleteClick, me)
            }
        ];
        
        me.callParent(arguments);

        me.addEvents(
            /**
             * @event deleteclick
             * Fires when the delete icon is clicked
             * @param {Ext.grid.View} gridView
             * @param {Number} rowIndex
             * @param {Number} colIndex
             * @param {Ext.grid.column.Action} column
             * @param {EventObject} e
             */
            'deleteclick',

            /**
             * @event taskdrop
             * Fires when a task record is dropped on this grid
             * @param {SimpleTasks.model.Task} task       The task record
             * @param {SimpleTasks.model.List} list       The list that the task was dropped on
             */
            'taskdrop',

            /**
             * @event listdrop
             * Fires when a list record is dropped on this grid
             * @param {SimpleTasks.model.List} list         The list that was dropped
             * @param {SimpleTasks.model.List} overList     The list that the list was dropped on
             * @param {String} position               `"before"` or `"after"` depending on whether the mouse is above or below the midline of the node.
             */
            'listdrop'
        );

        me.on('beforeedit', me.handleBeforeEdit, me);
        me.relayEvents(me.getView(), ['taskdrop', 'listdrop'])

    },

    /**
     * Handles a click on a delete icon
     * @private
     * @param {Ext.tree.View} treeView
     * @param {Number} rowIndex
     * @param {Number} colIndex
     * @param {Ext.grid.column.Action} column
     * @param {EventObject} e
     */
    handleDeleteClick: function(gridView, rowIndex, colIndex, column, e) {
        // Fire a "deleteclick" event with all the same args as this handler
        this.fireEvent('deleteclick', gridView, rowIndex, colIndex, column, e);
    },

    /**
     * Handles this grid's "beforeedit" event (relayed from the CellEditing plugin).
     * Prevents editing of "All Lists" root by returning false if the record has an id of -1
     * @private
     * @param {Ext.grid.plugin.CellEditing} editingPlugin       The cell editing plugin
     * @param {Object} e                                        an edit event object
     */
    handleBeforeEdit: function(editingPlugin, e) {
        return e.record.get('id') !== -1;
    },

    /**
     * Renderer for the name field.
     * Adds the task count after the list name.
     * @private
     * @param {String} value
     * @param {Object} metaData
     * @param {SimpleTasks.model.List} list
     * @param {Number} rowIndex
     * @param {Number} colIndex
     * @param {SimpleTasks.store.Lists} store
     * @param {Ext.grid.View} view
     */
    renderName: function(value, metaData, list, rowIndex, colIndex, store, view) {
        var tasksStore = Ext.StoreMgr.lookup('Tasks'),
            count = 0;

        (function countTasks(list) {
            count += tasksStore.queryBy(function(task, id) {
                // only show count for tasks that are not done
                return task.get('list_id') === list.get('id') && task.get('done') === false;
            }).getCount();

            list.eachChild(function(child) {
                countTasks(child);
            });
        })(list);

        return value + ' (' + count + ')';
    },

    /**
     * Triggers the list tree to refresh its view.  This is necessary in two scenarios:
     * 1) Since the lists and tasks are loaded asyncrounously, The Lists store may have finished
     *    loading before the tasks store.  In this case, the tasks data would not be available so all
     *    of the task counts would be rendered as (0).
     * 2) When a task is dragged and dropped onto a list, or when a list is deleted the task count won't automatially be updated
     *    because none of the data in the lists store actually changed (the renderer gets the count
     *    from the tasks store).
     *    
     * In both situations refreshing the lists view we ensure that the task counts are accurate.
     */
    refreshView: function() {
        // refresh the data in the view.  This will trigger the column renderers to run, making sure the task counts are up to date.
        this.getView().refresh();
    }



});