OLD | NEW |
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, 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 import 'dart:async'; | 5 import 'dart:async'; |
6 import 'dart:html'; | 6 import 'dart:html'; |
7 import 'package:observatory/src/elements/helpers/rendering_scheduler.dart'; | 7 import 'package:observatory/src/elements/helpers/rendering_scheduler.dart'; |
8 import 'package:observatory/src/elements/helpers/tag.dart'; | 8 import 'package:observatory/src/elements/helpers/tag.dart'; |
9 | 9 |
10 typedef HtmlElement VirtualCollectionCreateCallback(); | 10 typedef HtmlElement VirtualCollectionCreateCallback(); |
11 typedef void VirtualCollectionUpdateCallback(HtmlElement el, dynamic item, | 11 typedef void VirtualCollectionUpdateCallback(HtmlElement el, dynamic item, |
12 int index); | 12 int index); |
13 | 13 |
14 class VirtualCollectionElement extends HtmlElement implements Renderable { | 14 class VirtualCollectionElement extends HtmlElement implements Renderable { |
15 static const tag = | 15 static const tag = |
16 const Tag<VirtualCollectionElement>('virtual-collection'); | 16 const Tag<VirtualCollectionElement>('virtual-collection'); |
17 | 17 |
18 RenderingScheduler<VirtualCollectionElement> _r; | 18 RenderingScheduler<VirtualCollectionElement> _r; |
19 | 19 |
20 Stream<RenderedEvent<VirtualCollectionElement>> get onRendered => | 20 Stream<RenderedEvent<VirtualCollectionElement>> get onRendered => |
21 _r.onRendered; | 21 _r.onRendered; |
22 | 22 |
23 VirtualCollectionCreateCallback _create; | 23 VirtualCollectionCreateCallback _create; |
24 VirtualCollectionCreateCallback _createHeader; | 24 VirtualCollectionCreateCallback _createHeader; |
25 VirtualCollectionUpdateCallback _update; | 25 VirtualCollectionUpdateCallback _update; |
26 double _itemHeight; | 26 double _itemHeight; |
| 27 double _headerHeight = 0.0; |
27 int _top; | 28 int _top; |
28 double _height; | 29 double _height; |
29 List _items; | 30 List _items; |
30 StreamSubscription _onScrollSubscription; | 31 StreamSubscription _onScrollSubscription; |
31 StreamSubscription _onResizeSubscription; | 32 StreamSubscription _onResizeSubscription; |
32 | 33 |
33 List get items => _items; | 34 List get items => _items; |
34 | 35 |
35 set items(Iterable value) { | 36 set items(Iterable value) { |
36 _items = new List.unmodifiable(value); | 37 _items = new List.unmodifiable(value); |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
99 static const int _preload = 2; | 100 static const int _preload = 2; |
100 /// L = length of all the elements loaded | 101 /// L = length of all the elements loaded |
101 /// l = length of the visible area | 102 /// l = length of the visible area |
102 /// | 103 /// |
103 /// L = l + 2 * l / _preload | 104 /// L = l + 2 * l / _preload |
104 /// l = L * _preload / (_preload + 2) | 105 /// l = L * _preload / (_preload + 2) |
105 /// | 106 /// |
106 /// tail = l / _preload = L * 1 / (_preload + 2) = L * _inverse_preload | 107 /// tail = l / _preload = L * 1 / (_preload + 2) = L * _inverse_preload |
107 static const double _inverse_preload = 1 / (_preload + 2); | 108 static const double _inverse_preload = 1 / (_preload + 2); |
108 | 109 |
| 110 var _takeIntoView; |
| 111 |
| 112 void takeIntoView(item) { |
| 113 _takeIntoView = item; |
| 114 _r.dirty(); |
| 115 } |
| 116 |
109 void render() { | 117 void render() { |
110 if (children.isEmpty) { | 118 if (children.isEmpty) { |
111 children = [ | 119 children = [ |
112 _scroller | 120 _scroller |
113 ..children = [ | 121 ..children = [ |
114 _shifter | 122 _shifter |
115 ..children = [_create()] | 123 ..children = [_create()] |
116 ], | 124 ], |
117 ]; | 125 ]; |
118 if (_createHeader != null) { | 126 if (_createHeader != null) { |
119 _header.children = [_createHeader()]; | 127 _header.children = [_createHeader()]; |
120 _scroller.children.insert(0, _header); | 128 _scroller.children.insert(0, _header); |
| 129 _headerHeight = _header.children[0].getBoundingClientRect().height; |
121 } | 130 } |
122 _itemHeight = _shifter.children[0].getBoundingClientRect().height; | 131 _itemHeight = _shifter.children[0].getBoundingClientRect().height; |
123 _height = getBoundingClientRect().height; | 132 _height = getBoundingClientRect().height; |
124 } | 133 } |
| 134 |
| 135 if (_takeIntoView != null) { |
| 136 final index = items.indexOf(_takeIntoView); |
| 137 if (index >= 0) { |
| 138 final minScrollTop = _itemHeight * (index + 1) - _height; |
| 139 final maxScrollTop = _itemHeight * index; |
| 140 scrollTop = ((maxScrollTop - minScrollTop) / 2 + minScrollTop).floor(); |
| 141 } |
| 142 _takeIntoView = null; |
| 143 } |
| 144 |
125 final top = (scrollTop / _itemHeight).floor(); | 145 final top = (scrollTop / _itemHeight).floor(); |
126 | 146 |
127 _header.style.top = '${scrollTop}px'; | 147 _header.style.top = '${scrollTop}px'; |
128 _scroller.style.height = '${_itemHeight*(_items.length)}px'; | 148 _scroller.style.height = '${_itemHeight*(_items.length)+_headerHeight}px'; |
129 final tail_length = (_height / _itemHeight / _preload).ceil(); | 149 final tail_length = (_height / _itemHeight / _preload).ceil(); |
130 final length = tail_length * 2 + tail_length * _preload; | 150 final length = tail_length * 2 + tail_length * _preload; |
131 | 151 |
132 if (_shifter.children.length < length) { | 152 if (_shifter.children.length < length) { |
133 while (_shifter.children.length != length) { | 153 while (_shifter.children.length != length) { |
134 var e = _create(); | 154 var e = _create(); |
135 e..style.display = 'hidden'; | 155 e..style.display = 'hidden'; |
136 _shifter.children.add(e); | 156 _shifter.children.add(e); |
137 } | 157 } |
138 _top = null; // force update; | 158 _top = null; // force update; |
(...skipping 20 matching lines...) Expand all Loading... |
159 } | 179 } |
160 | 180 |
161 void _onResize(_) { | 181 void _onResize(_) { |
162 final newHeight = getBoundingClientRect().height; | 182 final newHeight = getBoundingClientRect().height; |
163 if (newHeight > _height) { | 183 if (newHeight > _height) { |
164 _height = newHeight; | 184 _height = newHeight; |
165 _r.dirty(); | 185 _r.dirty(); |
166 } | 186 } |
167 } | 187 } |
168 } | 188 } |
OLD | NEW |