| 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 |