Tree.js
6.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/**
* @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();
}
});