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( |
12 int index); | 12 HtmlElement el, dynamic item, 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 = const Tag<VirtualCollectionElement>('virtual-collection'); |
16 const Tag<VirtualCollectionElement>('virtual-collection'); | |
17 | 16 |
18 RenderingScheduler<VirtualCollectionElement> _r; | 17 RenderingScheduler<VirtualCollectionElement> _r; |
19 | 18 |
20 Stream<RenderedEvent<VirtualCollectionElement>> get onRendered => | 19 Stream<RenderedEvent<VirtualCollectionElement>> get onRendered => |
21 _r.onRendered; | 20 _r.onRendered; |
22 | 21 |
23 VirtualCollectionCreateCallback _create; | 22 VirtualCollectionCreateCallback _create; |
24 VirtualCollectionCreateCallback _createHeader; | 23 VirtualCollectionCreateCallback _createHeader; |
25 VirtualCollectionUpdateCallback _update; | 24 VirtualCollectionUpdateCallback _update; |
26 double _itemHeight; | 25 double _itemHeight; |
27 double _headerHeight = 0.0; | 26 double _headerHeight = 0.0; |
28 int _top; | 27 int _top; |
29 double _height; | 28 double _height; |
30 List _items; | 29 List _items; |
31 StreamSubscription _onScrollSubscription; | 30 StreamSubscription _onScrollSubscription; |
32 StreamSubscription _onResizeSubscription; | 31 StreamSubscription _onResizeSubscription; |
33 | 32 |
34 List get items => _items; | 33 List get items => _items; |
35 | 34 |
36 set items(Iterable value) { | 35 set items(Iterable value) { |
37 _items = new List.unmodifiable(value); | 36 _items = new List.unmodifiable(value); |
38 _top = null; | 37 _top = null; |
39 _r.dirty(); | 38 _r.dirty(); |
40 } | 39 } |
41 | 40 |
42 | |
43 factory VirtualCollectionElement(VirtualCollectionCreateCallback create, | 41 factory VirtualCollectionElement(VirtualCollectionCreateCallback create, |
44 VirtualCollectionUpdateCallback update, {Iterable items: const [], | 42 VirtualCollectionUpdateCallback update, |
| 43 {Iterable items: const [], |
45 VirtualCollectionCreateCallback createHeader, | 44 VirtualCollectionCreateCallback createHeader, |
46 RenderingQueue queue}) { | 45 RenderingQueue queue}) { |
47 assert(create != null); | 46 assert(create != null); |
48 assert(update != null); | 47 assert(update != null); |
49 assert(items != null); | 48 assert(items != null); |
50 VirtualCollectionElement e = document.createElement(tag.name); | 49 VirtualCollectionElement e = document.createElement(tag.name); |
51 e._r = new RenderingScheduler(e, queue: queue); | 50 e._r = new RenderingScheduler(e, queue: queue); |
52 e._create = create; | 51 e._create = create; |
53 e._createHeader = createHeader; | 52 e._createHeader = createHeader; |
54 e._update = update; | 53 e._update = update; |
55 e._items = new List.unmodifiable(items); | 54 e._items = new List.unmodifiable(items); |
56 return e; | 55 return e; |
57 } | 56 } |
58 | 57 |
59 VirtualCollectionElement.created() : super.created(); | 58 VirtualCollectionElement.created() : super.created(); |
60 | 59 |
61 @override | 60 @override |
62 attached() { | 61 attached() { |
63 super.attached(); | 62 super.attached(); |
64 _r.enable(); | 63 _r.enable(); |
65 _top = null; | 64 _top = null; |
66 _itemHeight = null; | 65 _itemHeight = null; |
67 _onScrollSubscription = onScroll.listen(_onScroll); | 66 _onScrollSubscription = onScroll.listen(_onScroll); |
68 _onResizeSubscription = window.onResize.listen(_onResize); | 67 _onResizeSubscription = window.onResize.listen(_onResize); |
69 } | 68 } |
70 | 69 |
71 @override | 70 @override |
72 detached() { | 71 detached() { |
73 super.detached(); | 72 super.detached(); |
74 _r.disable(notify: true); | 73 _r.disable(notify: true); |
75 children = const []; | 74 children = const []; |
76 _onScrollSubscription.cancel(); | 75 _onScrollSubscription.cancel(); |
77 _onResizeSubscription.cancel(); | 76 _onResizeSubscription.cancel(); |
78 } | 77 } |
79 | 78 |
80 final DivElement _header = new DivElement()..classes = ['header']; | 79 final DivElement _header = new DivElement()..classes = ['header']; |
81 final DivElement _scroller = new DivElement()..classes = ['scroller']; | 80 final DivElement _scroller = new DivElement()..classes = ['scroller']; |
82 final DivElement _shifter = new DivElement()..classes = ['shifter']; | 81 final DivElement _shifter = new DivElement()..classes = ['shifter']; |
83 | 82 |
84 dynamic getItemFromElement(HtmlElement element) { | 83 dynamic getItemFromElement(HtmlElement element) { |
85 final el_index = _shifter.children.indexOf(element); | 84 final el_index = _shifter.children.indexOf(element); |
86 if (el_index < 0) { | 85 if (el_index < 0) { |
87 return null; | 86 return null; |
88 } | 87 } |
89 final item_index = | 88 final item_index = |
90 _top + el_index - (_shifter.children.length * _inverse_preload).floor(); | 89 _top + el_index - (_shifter.children.length * _inverse_preload).floor(); |
91 if (0 <= item_index && item_index < items.length) { | 90 if (0 <= item_index && item_index < items.length) { |
92 return _items[item_index]; | 91 return _items[item_index]; |
93 } | 92 } |
94 return null; | 93 return null; |
95 } | 94 } |
96 | 95 |
97 /// The preloaded element before and after the visible area are: | 96 /// The preloaded element before and after the visible area are: |
98 /// 1/preload_size of the number of items in the visble area. | 97 /// 1/preload_size of the number of items in the visble area. |
99 /// See shared.css for the "top:-25%;". | 98 /// See shared.css for the "top:-25%;". |
100 static const int _preload = 2; | 99 static const int _preload = 2; |
| 100 |
101 /// L = length of all the elements loaded | 101 /// L = length of all the elements loaded |
102 /// l = length of the visible area | 102 /// l = length of the visible area |
103 /// | 103 /// |
104 /// L = l + 2 * l / _preload | 104 /// L = l + 2 * l / _preload |
105 /// l = L * _preload / (_preload + 2) | 105 /// l = L * _preload / (_preload + 2) |
106 /// | 106 /// |
107 /// tail = l / _preload = L * 1 / (_preload + 2) = L * _inverse_preload | 107 /// tail = l / _preload = L * 1 / (_preload + 2) = L * _inverse_preload |
108 static const double _inverse_preload = 1 / (_preload + 2); | 108 static const double _inverse_preload = 1 / (_preload + 2); |
109 | 109 |
110 var _takeIntoView; | 110 var _takeIntoView; |
111 | 111 |
112 void takeIntoView(item) { | 112 void takeIntoView(item) { |
113 _takeIntoView = item; | 113 _takeIntoView = item; |
114 _r.dirty(); | 114 _r.dirty(); |
115 } | 115 } |
116 | 116 |
117 void render() { | 117 void render() { |
118 if (children.isEmpty) { | 118 if (children.isEmpty) { |
119 children = [ | 119 children = [ |
120 _scroller | 120 _scroller |
121 ..children = [ | 121 ..children = [ |
122 _shifter | 122 _shifter..children = [_create()] |
123 ..children = [_create()] | |
124 ], | 123 ], |
125 ]; | 124 ]; |
126 if (_createHeader != null) { | 125 if (_createHeader != null) { |
127 _header.children = [_createHeader()]; | 126 _header.children = [_createHeader()]; |
128 _scroller.children.insert(0, _header); | 127 _scroller.children.insert(0, _header); |
129 _headerHeight = _header.children[0].getBoundingClientRect().height; | 128 _headerHeight = _header.children[0].getBoundingClientRect().height; |
130 } | 129 } |
131 _itemHeight = _shifter.children[0].getBoundingClientRect().height; | 130 _itemHeight = _shifter.children[0].getBoundingClientRect().height; |
132 _height = getBoundingClientRect().height; | 131 _height = getBoundingClientRect().height; |
133 } | 132 } |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
179 } | 178 } |
180 | 179 |
181 void _onResize(_) { | 180 void _onResize(_) { |
182 final newHeight = getBoundingClientRect().height; | 181 final newHeight = getBoundingClientRect().height; |
183 if (newHeight > _height) { | 182 if (newHeight > _height) { |
184 _height = newHeight; | 183 _height = newHeight; |
185 _r.dirty(); | 184 _r.dirty(); |
186 } | 185 } |
187 } | 186 } |
188 } | 187 } |
OLD | NEW |