Chromium Code Reviews| Index: runtime/observatory/lib/src/elements/containers/virtual_tree.dart |
| diff --git a/runtime/observatory/lib/src/elements/containers/virtual_tree.dart b/runtime/observatory/lib/src/elements/containers/virtual_tree.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..7ca9359bfd9cdb40a70a222eb0989fe5b988d4f7 |
| --- /dev/null |
| +++ b/runtime/observatory/lib/src/elements/containers/virtual_tree.dart |
| @@ -0,0 +1,135 @@ |
| +// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| +// for details. All rights reserved. Use of this source code is governed by a |
| +// BSD-style license that can be found in the LICENSE file. |
| + |
| +import 'dart:async'; |
| +import 'dart:html'; |
| +import 'package:observatory/src/elements/containers/virtual_collection.dart'; |
| +import 'package:observatory/src/elements/helpers/rendering_scheduler.dart'; |
| +import 'package:observatory/src/elements/helpers/tag.dart'; |
| + |
| +typedef HtmlElement _CreateElementCallback(toggle({bool forward})); |
|
Cutch
2016/08/01 17:02:36
don't these need to be public?
what is forward?
cbernaschina
2016/08/01 17:44:45
Made public and fixed collision in elements.dart w
|
| +typedef void _UpdateElementCallback(HtmlElement el, dynamic item, int depth); |
| +typedef Iterable<dynamic> _GetChildrenCallback(dynamic value); |
| + |
| +class VirtualTreeElement extends HtmlElement implements Renderable { |
| + static const tag = |
| + const Tag<VirtualTreeElement>('virtual-tree', dependencies: const [ |
| + VirtualCollectionElement.tag |
| + ]); |
| + |
| + RenderingScheduler<VirtualTreeElement> _r; |
| + |
| + Stream<RenderedEvent<VirtualTreeElement>> get onRendered => _r.onRendered; |
| + |
| + _GetChildrenCallback _children; |
| + List _items; |
| + List _depths; |
| + final Set _expanded = new Set(); |
| + |
| + List get items => _items; |
| + |
| + set items(Iterable value) { |
| + _items = new List.unmodifiable(value); |
| + _expanded.clear(); |
| + _r.dirty(); |
| + } |
| + |
| + factory VirtualTreeElement(_CreateElementCallback create, |
| + _UpdateElementCallback update, _GetChildrenCallback children, |
| + {Iterable items: const [], RenderingQueue queue}) { |
| + assert(create != null); |
| + assert(update != null); |
| + assert(children != null); |
| + assert(items != null); |
| + VirtualTreeElement e = document.createElement(tag.name); |
| + e._r = new RenderingScheduler(e, queue: queue); |
| + e._children = children; |
| + e._collection = new VirtualCollectionElement(() { |
| + var element; |
| + return element = create(({bool forward: false}) { |
| + var item = e._collection.getItemFromElement(element); |
| + if (e.isExpanded(item)) { |
| + e.collapse(item, forward: forward); |
| + } else { |
| + e.expand(item, forward: forward); |
| + } |
| + }); |
| + }, (HtmlElement el, dynamic item, int index) { |
| + update(el, item, e._depths[index]); |
| + }, queue: queue); |
| + e._items = new List.unmodifiable(items); |
| + return e; |
| + } |
| + |
| + VirtualTreeElement.created() : super.created(); |
| + |
| + bool isExpanded(item) { |
| + return _expanded.contains(item); |
| + } |
| + |
| + void expand(item, {bool forward: false}) { |
| + if (_expanded.add(item)) _r.dirty(); |
| + if (forward) { |
| + var children = _children(item); |
| + while (children.length == 1) { |
| + _expanded.add(children.first); |
| + children = _children(children.first); |
| + } |
| + } |
| + } |
| + |
| + void collapse(item, {bool forward: false}) { |
| + if (_expanded.remove(item)) _r.dirty(); |
| + if (forward) { |
| + var children = _children(item); |
| + while (children.length == 1) { |
| + _expanded.remove(children.first); |
| + children = _children(children.first); |
| + } |
| + } |
| + } |
| + |
| + @override |
| + attached() { |
| + super.attached(); |
| + _r.enable(); |
| + } |
| + |
| + @override |
| + detached() { |
| + super.detached(); _r.disable(notify: true); |
| + children = const []; |
| + } |
| + |
| + VirtualCollectionElement _collection; |
| + |
| + void render() { |
| + if (children.length == 0) { |
| + children = [_collection]; |
| + } |
| + Iterable _toList(item) { |
| + if (isExpanded(item)) { |
| + Iterable children = _children(item); |
| + if (children.isNotEmpty) { |
| + return [item]..addAll(children.expand(_toList)); |
| + } |
| + } |
| + return [item]; |
| + } |
| + _collection.items = _items.expand(_toList); |
| + var depth = 0; |
| + Iterable _toDepth(item) { |
| + if (isExpanded(item)) { |
| + Iterable children = _children(item); |
| + if (children.isNotEmpty) { |
| + depth++; |
| + return children.expand(_toDepth).toList() |
| + ..insert(0, --depth); |
| + } |
| + } |
| + return [depth]; |
| + } |
| + _depths = _items.expand(_toDepth).toList(); |
| + } |
| +} |