OLD | NEW |
---|---|
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 part of app; | 5 part of app; |
6 | 6 |
7 abstract class TableTreeRow extends Observable { | 7 abstract class TableTreeRow extends Observable { |
8 static const arrowRight = '\u2192'; | 8 static const arrowRight = '\u2192'; |
9 static const arrowDownRight = '\u21b3'; | 9 static const arrowDownRight = '\u21b3'; |
10 // Number of pixels each subtree is indented. | 10 // Number of ems each subtree is indented. |
11 static const subtreeIndent = 16; | 11 static const subtreeIndent = 2; |
12 | |
13 TableTreeRow(this.tree, TableTreeRow parent) : | |
14 parent = parent, | |
15 depth = parent != null ? parent.depth + 1 : 0 { | |
16 } | |
12 | 17 |
13 final TableTree tree; | 18 final TableTree tree; |
14 final TableTreeRow parent; | 19 final TableTreeRow parent; |
15 final int depth; | 20 final int depth; |
16 final List<TableTreeRow> children = new List<TableTreeRow>(); | 21 final List<TableTreeRow> children = new List<TableTreeRow>(); |
17 final List<TableCellElement> tableColumns = new List<TableCellElement>(); | 22 final List<TableCellElement> _tableColumns = new List<TableCellElement>(); |
23 final List<DivElement> flexColumns = new List<DivElement>(); | |
18 SpanElement _expander; | 24 SpanElement _expander; |
19 TableRowElement _tr; | 25 TableRowElement _tr; |
20 TableRowElement get tr { | 26 TableRowElement get tr => _tr; |
21 assert(_tr != null); | |
22 return _tr; | |
23 } | |
24 | |
25 TableTreeRow(this.tree, TableTreeRow parent) : | |
26 parent = parent, | |
27 depth = parent != null ? parent.depth+1 : 0 { | |
28 } | |
29 | |
30 bool _expanded = false; | 27 bool _expanded = false; |
31 bool get expanded => _expanded; | 28 bool get expanded => _expanded; |
32 set expanded(bool expanded) { | 29 set expanded(bool expanded) { |
33 var changed = _expanded != expanded; | 30 var changed = _expanded != expanded; |
34 _expanded = expanded; | 31 _expanded = expanded; |
35 if (changed) { | 32 if (changed) { |
36 // If the state has changed, fire callbacks. | 33 // If the state has changed, fire callbacks. |
37 if (_expanded) { | 34 if (_expanded) { |
38 _onExpand(); | 35 _onExpand(); |
39 } else { | 36 } else { |
40 _onCollapse(); | 37 _onCollapse(); |
41 } | 38 } |
42 } | 39 } |
43 } | 40 } |
44 | 41 |
45 bool expandOrCollapse() { | 42 /// Fired when the tree row is being expanded. |
43 void _onExpand() { | |
44 _updateExpanderView(); | |
45 } | |
46 | |
47 /// Fired when the tree row is being collapsed. | |
48 void _onCollapse() { | |
49 for (var child in children) { | |
50 child.onHide(); | |
51 } | |
52 _updateExpanderView(); | |
53 } | |
54 | |
55 bool toggle() { | |
46 expanded = !expanded; | 56 expanded = !expanded; |
47 return expanded; | 57 return expanded; |
48 } | 58 } |
49 | 59 |
50 bool hasChildren(); | 60 HtmlElement _makeColorBlock(String backgroundColor) { |
61 var colorBlock = new DivElement(); | |
62 colorBlock.style.minWidth = '2px'; | |
63 colorBlock.style.backgroundColor = backgroundColor; | |
64 return colorBlock; | |
65 } | |
51 | 66 |
52 String _backgroundColorClassForRow() { | 67 HtmlElement _makeExpander() { |
53 const colors = const ['rowColor0', 'rowColor1', 'rowColor2', 'rowColor3', | 68 var expander = new SpanElement(); |
54 'rowColor4', 'rowColor5', 'rowColor6', 'rowColor7', | 69 expander.style.minWidth = '1.5em'; |
55 'rowColor8']; | 70 expander.onClick.listen(onClick); |
56 var index = (depth - 1) % colors.length; | 71 return expander; |
57 return colors[index]; | 72 } |
73 | |
74 void onClick(Event e) { | |
75 e.stopPropagation(); | |
76 tree.toggle(this); | |
58 } | 77 } |
59 | 78 |
60 void _buildRow() { | 79 void _buildRow() { |
80 const List backgroundColors = const [ | |
81 '#F44336', | |
82 '#3F51B5', | |
83 '#673AB7', | |
84 '#4CAF50', | |
85 '#FF9800', | |
siva
2015/03/02 21:58:44
More readable const variables for these values?
Cutch
2015/03/02 22:48:19
Done.
| |
86 ]; | |
61 _tr = new TableRowElement(); | 87 _tr = new TableRowElement(); |
62 for (var i = 0; i < tree.columnCount; i++) { | 88 for (var i = 0; i < tree.columnCount; i++) { |
63 var cell = _tr.insertCell(-1); | 89 var cell = _tr.insertCell(-1); |
64 cell.classes.add(_backgroundColorClassForRow()); | 90 _tableColumns.add(cell); |
65 tableColumns.add(cell); | 91 var flex = new DivElement(); |
92 flex.classes.add('flex-row'); | |
93 cell.children.add(flex); | |
94 flexColumns.add(flex); | |
66 } | 95 } |
67 var firstColumn = tableColumns[0]; | 96 var firstColumn = flexColumns[0]; |
68 var columnContainer = new DivElement(); | 97 _tableColumns[0].style.paddingLeft = '${(depth - 1) * subtreeIndent}em'; |
69 columnContainer.classes.add('flex-row'); | 98 var backgroundColor = '#FAFAFA'; |
siva
2015/03/02 21:58:44
Ditto comment.
Cutch
2015/03/02 22:48:19
Done.
| |
70 _expander = new SpanElement(); | 99 if (depth > 1) { |
71 _expander.style.display = 'inline-block'; | 100 var colorIndex = (depth - 1) % backgroundColors.length; |
72 _expander.style.display = 'inline-block'; | 101 backgroundColor = backgroundColors[colorIndex]; |
73 _expander.style.minWidth = '1.5em'; | 102 } |
74 _expander.onClick.listen(onClick); | 103 var colorBlock = _makeColorBlock(backgroundColor); |
75 columnContainer.children.add(_expander); | 104 firstColumn.children.add(colorBlock); |
76 firstColumn.style.paddingLeft = '${depth * subtreeIndent}px'; | 105 _expander = _makeExpander(); |
77 firstColumn.children.add(columnContainer); | 106 firstColumn.children.add(_expander); |
78 updateExpanderView(); | 107 // Enable expansion by clicking anywhere on the first column. |
108 firstColumn.onClick.listen(onClick); | |
109 _updateExpanderView(); | |
79 } | 110 } |
80 | 111 |
81 void updateExpanderView() { | 112 void _updateExpanderView() { |
82 if (_expander == null) { | 113 if (_expander == null) { |
83 return; | 114 return; |
84 } | 115 } |
85 if (!hasChildren()) { | 116 if (!hasChildren()) { |
86 _expander.style.visibility = 'hidden'; | 117 _expander.style.visibility = 'hidden'; |
87 _expander.style.cursor = 'auto'; | 118 _expander.classes.remove('pointer'); |
88 return; | 119 return; |
89 } else { | 120 } else { |
90 _expander.style.visibility = 'visible'; | 121 _expander.style.visibility = 'visible'; |
91 _expander.style.cursor = 'pointer'; | 122 _expander.classes.add('pointer'); |
92 } | 123 } |
93 _expander.text = expanded ? arrowDownRight : arrowRight; | 124 _expander.children.clear(); |
125 _expander.children.add(expanded ? | |
126 new Element.tag('icon-expand-more') : | |
127 new Element.tag('icon-chevron-right')); | |
94 } | 128 } |
95 | 129 |
130 bool hasChildren(); | |
131 | |
96 /// Fired when the tree row is being shown. | 132 /// Fired when the tree row is being shown. |
97 /// Populate tr and add logical children here. | 133 /// Populate tr and add logical children here. |
98 void onShow() { | 134 void onShow() { |
99 assert(_tr == null); | 135 assert(_tr == null); |
100 _buildRow(); | 136 _buildRow(); |
101 } | 137 } |
102 | 138 |
103 /// Fired when the tree row is being hidden. | 139 /// Fired when the tree row is being hidden. |
104 void onHide() { | 140 void onHide() { |
105 assert(_tr != null); | |
106 _tr = null; | 141 _tr = null; |
107 tableColumns.clear(); | |
108 _expander = null; | 142 _expander = null; |
109 } | 143 if (_tableColumns != null) { |
110 | 144 _tableColumns.clear(); |
111 /// Fired when the tree row is being expanded. | |
112 void _onExpand() { | |
113 for (var child in children) { | |
114 child.onShow(); | |
115 child.updateExpanderView(); | |
116 } | 145 } |
117 updateExpanderView(); | 146 if (flexColumns != null) { |
118 } | 147 flexColumns.clear(); |
119 | |
120 /// Fired when the tree row is being collapsed. | |
121 void _onCollapse() { | |
122 for (var child in children) { | |
123 child.onHide(); | |
124 } | 148 } |
125 updateExpanderView(); | |
126 } | |
127 | |
128 void onClick(Event e) { | |
129 tree.toggle(this); | |
130 e.stopPropagation(); | |
131 } | 149 } |
132 } | 150 } |
133 | 151 |
134 class TableTree extends Observable { | 152 class TableTree extends Observable { |
135 final TableSectionElement tableBody; | 153 final TableSectionElement tableBody; |
136 final List<TableTreeRow> rows = []; | 154 final List<TableTreeRow> rows = []; |
137 final int columnCount; | 155 final int columnCount; |
138 | 156 var _operation; |
rmacnak
2015/03/02 22:25:26
Consider Future _pendingOperation;
Cutch
2015/03/02 22:48:19
Done.
| |
139 /// Create a table tree with column [headers]. | 157 /// Create a table tree with column [headers]. |
140 TableTree(this.tableBody, this.columnCount); | 158 TableTree(this.tableBody, this.columnCount); |
141 | 159 |
142 void clear() { | 160 void clear() { |
143 tableBody.children.clear(); | 161 tableBody.children.clear(); |
144 rows.clear(); | 162 rows.clear(); |
145 } | 163 } |
146 | 164 |
147 /// Initialize the table tree with the list of root children. | 165 /// Initialize the table tree with the list of root children. |
148 void initialize(TableTreeRow root) { | 166 void initialize(TableTreeRow root) { |
149 clear(); | 167 clear(); |
150 root.onShow(); | 168 root.onShow(); |
151 rows.addAll(root.children); | 169 toggle(root); |
152 for (var i = 0; i < rows.length; i++) { | |
153 rows[i].onShow(); | |
154 tableBody.children.add(rows[i].tr); | |
155 } | |
156 } | 170 } |
157 | 171 |
158 /// Toggle expansion of row in tree. | 172 /// Toggle expansion of row in tree. |
159 void toggle(TableTreeRow row) { | 173 toggle(TableTreeRow row) async { |
160 if (row.expandOrCollapse()) { | 174 if (_operation != null) { |
161 _expand(row); | 175 return; |
176 } | |
177 if (row.toggle()) { | |
178 document.body.classes.add('busy'); | |
179 _operation = _expand(row); | |
180 await _operation; | |
181 _operation = null; | |
182 document.body.classes.remove('busy'); | |
183 if (row.children.length == 1) { | |
184 // Auto expand single child. | |
185 await toggle(row.children[0]); | |
186 } | |
162 } else { | 187 } else { |
163 _collapse(row); | 188 document.body.classes.add('busy'); |
189 _operation = _collapse(row); | |
190 await _operation; | |
191 _operation = null; | |
192 document.body.classes.remove('busy'); | |
164 } | 193 } |
165 } | 194 } |
166 | 195 |
167 int _index(TableTreeRow row) => rows.indexOf(row); | 196 int _index(TableTreeRow row) => rows.indexOf(row); |
168 | 197 |
169 void _expand(TableTreeRow row) { | 198 _insertRow(index, child) { |
199 rows.insert(index, child); | |
200 tableBody.children.insert(index, child.tr); | |
201 } | |
202 | |
203 _expand(TableTreeRow row) async { | |
170 int index = _index(row); | 204 int index = _index(row); |
171 assert(index != -1); | 205 if ((index == -1) && (rows.length != 0)) { |
172 rows.insertAll(index + 1, row.children); | 206 return; |
173 for (var i = 0; i < row.children.length; i++) { | 207 } |
174 tableBody.children.insert(index + i + 1, row.children[i].tr); | 208 assert((index != -1) || (rows.length == 0)); |
209 var i = 0; | |
210 var addPerIteration = 2; | |
211 while (i < row.children.length) { | |
212 await window.animationFrame; | |
213 for (var j = 0; j < addPerIteration; j++) { | |
214 if (i == row.children.length) { | |
215 break; | |
216 } | |
217 var child = row.children[i]; | |
218 child.onShow(); | |
219 child._updateExpanderView(); | |
220 _insertRow(index + i + 1, row.children[i]); | |
rmacnak
2015/03/02 22:25:26
row.children[i] -> child
Cutch
2015/03/02 22:48:19
Done.
| |
221 i++; | |
222 } | |
175 } | 223 } |
176 } | 224 } |
177 | 225 |
178 void _collapse(TableTreeRow row) { | 226 _collapseSync(TableTreeRow row) { |
179 var childCount = row.children.length; | 227 var childCount = row.children.length; |
180 if (childCount == 0) { | 228 if (childCount == 0) { |
181 return; | 229 return; |
182 } | 230 } |
183 for (var i = 0; i < childCount; i++) { | 231 for (var i = 0; i < childCount; i++) { |
184 // Close all inner rows. | 232 // Close all inner rows. |
185 if (row.children[i].expanded) { | 233 if (row.children[i].expanded) { |
186 _collapse(row.children[i]); | 234 _collapseSync(row.children[i]); |
187 } | 235 } |
188 } | 236 } |
189 // Collapse this row. | 237 // Collapse this row. |
190 row.expanded = false; | 238 row.expanded = false; |
191 // Remove all children. | 239 // Remove all children. |
192 int index = _index(row); | 240 int index = _index(row); |
193 rows.removeRange(index + 1, index + 1 + childCount); | |
194 for (var i = 0; i < childCount; i++) { | 241 for (var i = 0; i < childCount; i++) { |
242 rows.removeAt(index + 1); | |
195 tableBody.children.removeAt(index + 1); | 243 tableBody.children.removeAt(index + 1); |
196 } | 244 } |
197 } | 245 } |
246 | |
247 _collapse(TableTreeRow row) async { | |
248 _collapseSync(row); | |
249 } | |
198 } | 250 } |
199 | 251 |
200 typedef String ValueFormatter(dynamic value); | 252 typedef String ValueFormatter(dynamic value); |
201 | 253 |
202 class SortedTableColumn { | 254 class SortedTableColumn { |
203 static String toStringFormatter(dynamic v) { | 255 static String toStringFormatter(dynamic v) { |
204 return v != null ? v.toString() : '<null>'; | 256 return v != null ? v.toString() : '<null>'; |
205 } | 257 } |
206 final String label; | 258 final String label; |
207 final ValueFormatter formatter; | 259 final ValueFormatter formatter; |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
290 if (column != _sortColumnIndex) { | 342 if (column != _sortColumnIndex) { |
291 return columns[column].label + '\u2003'; | 343 return columns[column].label + '\u2003'; |
292 } | 344 } |
293 return columns[column].label + (_sortDescending ? arrowUp : arrowDown); | 345 return columns[column].label + (_sortDescending ? arrowUp : arrowDown); |
294 } | 346 } |
295 | 347 |
296 dynamic getValue(int row, int column) { | 348 dynamic getValue(int row, int column) { |
297 return rows[row].values[column]; | 349 return rows[row].values[column]; |
298 } | 350 } |
299 } | 351 } |
OLD | NEW |