| Index: sky/examples/fn/lib/component.dart
|
| diff --git a/sky/examples/fn/lib/component.dart b/sky/examples/fn/lib/component.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..1263b416252b540fcc7184dc524e5620ca907873
|
| --- /dev/null
|
| +++ b/sky/examples/fn/lib/component.dart
|
| @@ -0,0 +1,158 @@
|
| +part of fn;
|
| +
|
| +List<Component> _dirtyComponents = new List<Component>();
|
| +bool _renderScheduled = false;
|
| +
|
| +void _renderDirtyComponents() {
|
| + Stopwatch sw = new Stopwatch()..start();
|
| +
|
| + _dirtyComponents.sort((a, b) => a._order - b._order);
|
| + for (var comp in _dirtyComponents) {
|
| + comp._renderIfDirty();
|
| + }
|
| +
|
| + _dirtyComponents.clear();
|
| + _renderScheduled = false;
|
| + sw.stop();
|
| + print("Render took ${sw.elapsedMicroseconds} microseconds");
|
| +}
|
| +
|
| +void _scheduleComponentForRender(Component c) {
|
| + _dirtyComponents.add(c);
|
| +
|
| + if (!_renderScheduled) {
|
| + _renderScheduled = true;
|
| + new Future.microtask(_renderDirtyComponents);
|
| + }
|
| +}
|
| +
|
| +abstract class Component extends Node {
|
| + bool _dirty = true; // components begin dirty because they haven't rendered.
|
| + Node _rendered = null;
|
| + bool _removed = false;
|
| + int _order;
|
| + static int _currentOrder = 0;
|
| + bool _stateful;
|
| + static Component _currentlyRendering;
|
| +
|
| + Component({ Object key, bool stateful })
|
| + : _stateful = stateful != null ? stateful : false,
|
| + _order = _currentOrder + 1,
|
| + super(key:key);
|
| +
|
| + void willUnmount() {}
|
| +
|
| + void _remove() {
|
| + assert(_rendered != null);
|
| + assert(_root != null);
|
| + willUnmount();
|
| + _rendered._remove();
|
| + _rendered = null;
|
| + _root = null;
|
| + _removed = true;
|
| + }
|
| +
|
| + // TODO(rafaelw): It seems wrong to expose DOM at all. This is presently
|
| + // needed to get sizing info.
|
| + sky.Node getRoot() => _root;
|
| +
|
| + bool _sync(Node old, sky.Node host, sky.Node insertBefore) {
|
| + Component oldComponent = old as Component;
|
| +
|
| + if (oldComponent == null || oldComponent == this) {
|
| + _renderInternal(host, insertBefore);
|
| + return false;
|
| + }
|
| +
|
| + assert(oldComponent != null);
|
| + assert(_dirty);
|
| + assert(_rendered == null);
|
| +
|
| + if (oldComponent._stateful) {
|
| + _stateful = false; // prevent iloop from _renderInternal below.
|
| +
|
| + reflect.copyPublicFields(this, oldComponent);
|
| +
|
| + oldComponent._dirty = true;
|
| + _dirty = false;
|
| +
|
| + oldComponent._renderInternal(host, insertBefore);
|
| + return true; // Must retain old component
|
| + }
|
| +
|
| + _rendered = oldComponent._rendered;
|
| + _renderInternal(host, insertBefore);
|
| + return false;
|
| + }
|
| +
|
| + void _renderInternal(sky.Node host, sky.Node insertBefore) {
|
| + if (!_dirty) {
|
| + assert(_rendered != null);
|
| + return;
|
| + }
|
| +
|
| + var oldRendered = _rendered;
|
| + int lastOrder = _currentOrder;
|
| + _currentOrder = _order;
|
| + _currentlyRendering = this;
|
| + _rendered = render();
|
| + _currentlyRendering = null;
|
| + _currentOrder = lastOrder;
|
| +
|
| + _dirty = false;
|
| +
|
| + // TODO(rafaelw): This prevents components from returning different node
|
| + // types as their root node at different times. Consider relaxing.
|
| + assert(oldRendered == null ||
|
| + _rendered.runtimeType == oldRendered.runtimeType);
|
| +
|
| + if (_rendered._sync(oldRendered, host, insertBefore)) {
|
| + _rendered = oldRendered; // retain stateful component
|
| + }
|
| + _root = _rendered._root;
|
| + assert(_rendered._root is sky.Node);
|
| + }
|
| +
|
| + void _renderIfDirty() {
|
| + assert(_rendered != null);
|
| + assert(!_removed);
|
| +
|
| + var rendered = _rendered;
|
| + while (rendered is Component) {
|
| + rendered = rendered._rendered;
|
| + }
|
| + sky.Node root = rendered._root;
|
| +
|
| + _renderInternal(root.parentNode, root.nextSibling);
|
| + }
|
| +
|
| + void setState(Function fn()) {
|
| + assert(_rendered != null); // cannot setState before mounting.
|
| + _stateful = true;
|
| + fn();
|
| + if (_currentlyRendering != this) {
|
| + _dirty = true;
|
| + _scheduleComponentForRender(this);
|
| + }
|
| + }
|
| +
|
| + Node render();
|
| +}
|
| +
|
| +abstract class App extends Component {
|
| + sky.Node _host = null;
|
| + App()
|
| + : super(stateful: true) {
|
| +
|
| + _host = sky.document.createElement('div');
|
| + sky.document.appendChild(_host);
|
| +
|
| + new Future.microtask(() {
|
| + Stopwatch sw = new Stopwatch()..start();
|
| + _sync(null, _host, null);
|
| + assert(_root is sky.Node);
|
| + sw.stop();
|
| + print("Initial render: ${sw.elapsedMicroseconds} microseconds");
|
| + });
|
| + }
|
| +}
|
|
|