Skip to content

Commit

Permalink
misc small fixes/improvements & initial famousEach implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
gadicc committed Jun 5, 2015
1 parent 0a9dc0b commit 632f1b6
Show file tree
Hide file tree
Showing 14 changed files with 390 additions and 21 deletions.
4 changes: 4 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
## vNEXT

* Fix wrapper with no args' renderFunc using `with` data as args
* famousEach, in a totally new and extensible way
* Some internal changes in how nodes are dismounted

## v1.0.0

PLEASE NOTE, this is not a "1.0.0 production quality release". Meteor packages
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -325,3 +325,7 @@ Besides the underlying Famous API and how we deal with it:
subtemplating. In the new version we just use `_onRender` and `_eventMap`
attributes, provided from the enclosing Templates single helper. Feedback
welcome. You can still use regular inclusion `{{>template}}` like usual.

* In v0 a MeteorFamousView was (usually) a kind of renderNode too. In v1
we maintain a separatation, we maintain our own tree (like we had to
in v0 too) but add famous nodes directly to the scene graph.
7 changes: 7 additions & 0 deletions lib/famous-views.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,10 @@ FView.ready(function() {
// XXX
FamousEngine.init();
});

unusedDiv = null;
FView.ready(function() {
// this will always be empty, but Blaze needs a real div to render to.
// let's make sure we only ping the dom once and don't garbage collect
unusedDiv = FView.unusedDiv = document.createElement('div');
});
123 changes: 123 additions & 0 deletions lib/famousEach.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
var OP = {
NULL: 0,
ADDED_AT: 1,
REMOVED_AT: 2
};

var currentOp = { type: OP.NULL };

function famousEachRender(eachView, template, argFunc) {
var fview = eachView._fview;
var children = fview.eachChildren = []; // each contentBlock instance

// For Blaze.currentView (see blaze/builtins.js#each)
eachView.argVar = new Blaze.ReactiveVar();
eachView.autorun(function () {
eachView.argVar.set(argFunc());
}, eachView.parentView);

eachView.stopHandle = ObserveSequence.observe(function () {
return eachView.argVar.get();
}, {
addedAt: function (id, item, index) {
// Deferred since could trigger any time and Blaze stuff takes time
FView.defer(function() {
var newItemView = Blaze.With(item, function() {
return template.constructView();
});

/*
* During Blaze.render(), newNode.addToParent() calls
* FamousEach.addChild() which calls GrandParent.addedAt()
* using the stored data below.
*/
currentOp.type = OP.ADDED_AT;
currentOp.index = index;
currentOp.id = id;
currentOp.item = item;
children.splice(index, 0, newItemView);
Blaze.render(newItemView, unusedDiv, eachView);
currentOp.type = OP.NULL;
});
},
removedAt: function (id, item, index) {
// Deferred since could trigger any time and Blaze stuff takes time
FView.defer(function() {
// same deal as addedAt
currentOp.type = OP.REMOVED_AT;
currentOp.index = index;
currentOp.id = id;
currentOp.item = item;
Blaze.remove(children[index]);
children.splice(index, 1);
currentOp.type = OP.NULL;
});
},
changedAt: function (id, newItem, oldItem, index) {
// Deferred only to maintain order with addedAt/removedAt
FView.defer(function() {
children[index].dataVar.set(newItem);
fview.parent.fvClass.changedAt.call(fview.parent,
id, newItem, oldItem, index);
});
},
movedTo: function (id, doc, fromIndex, toIndex) {
// Deferred only to maintain order with addedAt/removedAt
FView.defer(function() {
item = children.splice(fromIndex, 1)[0];
children.splice(toIndex, 0, item);
fview.parent.fvClass.movedTo.call(fview.parent,
id, doc, fromIndex, toIndex);
});
}
});
}

var famousEachClass = {

addChild: function(child) {
if (currentOp.type !== OP.ADDED_AT)
throw new Error("we broke :(");
// this = fview
// need to diff famousEach position in child list
this.parent.fvClass.addedAt.call(this.parent,
currentOp.id, child, currentOp.index);
},

removeChild: function(child) {
if (currentOp.type !== OP.REMOVED_AT)
throw new Error("we broke :(");
// this = fview
// need to diff famousEach position in child list
this.parent.fvClass.removedAt.call(this.parent,
currentOp.id, child, currentOp.index);
}

};

function famousEachCreated() {
var blazeView = this.view;
var fview = new MeteorFamousView(fviewParentFromBlazeView(blazeView),
null /* id */, 'FamousEach', parentDetails(blazeView));
blazeView._fview = fview;
fview.fvClass = famousEachClass;

// Contents of {{#famousEach}}block{{/famousEach}}
if (blazeView.templateContentBlock)
famousEachRender(blazeView, blazeView.templateContentBlock, function() {
return Blaze.getData(blazeView);
});
}

function famousEachDestroyed() {
this.view._fview.destroy(true);
}

var famousEachView = new Blaze.Template(
'FamousEach',
function() { return null; }
);
famousEachView.created = famousEachCreated;
famousEachView.destroyed = famousEachDestroyed;

Blaze.registerHelper('famousEach', famousEachView);
11 changes: 9 additions & 2 deletions lib/meteorFamousView.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ var fviewCount = FView._fviewCount = 0;
* @property {MeteorFamousView} parent
* @property {array} children
*/
MeteorFamousView = FView._MeteorFamousView = function(parent, id, type, source) {
MeteorFamousView = FView._MeteorFamousView =
function MeteorFamousView(parent, id, type, source) {
this.id = id || fviewCount;
fviews[this.id] = this;
fviewCount++;
Expand Down Expand Up @@ -37,14 +38,20 @@ MeteorFamousView.prototype.destroy = function() {
log.debug("Destroying " + this.type + " (#" + this.id + ") from " + this._source);

// TODO children, etc.

// remove from parent
if (this.parent)
this.parent.children.splice(this.parent.children.indexOf(this), 1);

this.fvClass.dismount.call(this);
};

FView.byId = function(id) {
return fviews[id];
};

fviewParentFromBlazeView = FView._fviewParentFromBlazeView = function (blazeView) {
while ((blazeView = blazeView.parentView) && blazeView.name.substr(0,7) !== 'Famous.');
while ((blazeView = blazeView.parentView) && blazeView.name.substr(0,6) !== 'Famous');
return blazeView && blazeView._fview;
};

Expand Down
7 changes: 5 additions & 2 deletions lib/wrappers/Components.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ var ComponentClass = {
// Note, we can't re-use Node's renderFunc, since this is a COMPONENT *on* the node
// However, we should modularize common code.
renderFunc: function() {
var blazeView = this;
if (blazeView.parentView && !blazeView.parentView.__isTemplateWith)
return null; // No arguments, data is from a `with`

var data = Blaze.getData(this);
if (!data)
return null;
return null; // No data context, i.e. no arguments

var blazeView = this;
var tpl = blazeView._templateInstance;
//var fview = blazeView._fview;
var fview = fviewParentFromBlazeView(blazeView);
Expand Down
93 changes: 81 additions & 12 deletions lib/wrappers/Nodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,74 @@ var NodeClass = FView._NodeClass = {
name: 'UnnamedNode',
famousClass: null,

/*
* Create a new Famous class instance, e.g. var node = new Node();
*/
newInstance: function(fview) {
var fview = this;
return new fview.fvClass.famousClass();
// var fview = this;
return new this.fvClass.famousClass();
},

/*
* Add a Famous child to the current fview
*/
addChild: function(child) {
var fview = this;
fview.node.addChild(child);
// var fview = this;
this.node.addChild(child);
},

/*
* How we are added to our parent
*/
addToParent: function() {
var fview = this;
fview.parent.fvClass.addChild.call(fview.parent, fview.node)
// var fview = this;
this.parent.fvClass.addChild.call(this.parent, this.node)
},

/*
* For observes, an add relative to a sequence
*/
addedAt: function(id, child, index) {
// var fview = this;
this.fvClass.addChild.call(this, child);
},

/*
* How to remove a famous child
*/
removeChild: function(child) {
// var fview = this;
this.node.removeChild(child);
},

/*
* What to do with our node when an fview instance is destroyed (usually
* via Template destroy). We call parent.removeChild() rather than
* the node's dismount() directly, to let the parent decide what to do.
*
*/
dismount: function() {
// var fview = this;
this.parent.fvClass.removeChild.call(this.parent, this.node);
},

/*
* Called when removed from an observe sequence
*/
removedAt: function(id, child, index) {
// var fview = this;
this.fvClass.removeChild.call(this, child);
},

/*
* Regular nodes don't care about this, views/layouts will
*/
changedAt: function() {},
movedTo: function() {},

/*
* How to handle reactive attribute changes
*/
/*
attrUpdate: function(key, value, oldValue, data, firstTime) {
var fview = this;
Expand All @@ -27,6 +80,9 @@ var NodeClass = FView._NodeClass = {
},
*/

/*
* How to construct our template
*/
makeTemplate: function(fvClass) {
var template = new Blaze.Template('Famous.' + fvClass.name,
fvClass.renderFunc);
Expand All @@ -36,12 +92,18 @@ var NodeClass = FView._NodeClass = {
return template;
},

/*
* The Blaze "renderFunc" that reruns reactively, used to call attrUpdate
*/
renderFunc: function() {
var blazeView = this;
if (blazeView.parentView && !blazeView.parentView.__isTemplateWith)
return null; // No arguments, data is from a `with`

var data = Blaze.getData(this);
if (!data)
return null;
return null; // No data context, i.e. no arguments

var blazeView = this;
var tpl = blazeView._templateInstance;
var fview = blazeView._fview;
var fvClass = blazeView.template._fviewClass;
Expand Down Expand Up @@ -69,6 +131,9 @@ var NodeClass = FView._NodeClass = {
return null;
},

/*
* What to do on a Blaze template created lifeycle callback
*/
templateCreated: function() {
var blazeView = this.view;
var data = this.data || {};
Expand Down Expand Up @@ -96,7 +161,7 @@ var NodeClass = FView._NodeClass = {
viewToRender = fview.blazeView.templateContentBlock;

// This materializes the Node's spacebars contents; div is never used
Blaze.render(viewToRender, document.createElement('div'), fview.blazeView);
Blaze.render(viewToRender, unusedDiv, fview.blazeView);

if (data._onRender) {
var onRender = getHelperFunc(blazeView, data._onRender);
Expand All @@ -108,10 +173,11 @@ var NodeClass = FView._NodeClass = {
}
},

/*
* What to do on a Blaze template destroyed lifecycle callback
*/
templateDestroyed: function() {
var fview = FView.from(this);
fview.destroy();
fview.node.dismount();
this.view._fview.destroy(true);
}
};

Expand All @@ -138,6 +204,9 @@ function argsFromVecTransitionCB(vec, transition, callback) {
return args;
}

/*
* Default attrUpdate for a typical node
*/
NodeClass.attrUpdate = function(key, value, oldValue, data, firstTime) {
var fview = this;

Expand Down
6 changes: 5 additions & 1 deletion lib/wrappers/Nodes/Scene.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ FView.wrap('Scene', null, {
return template;
},

dismount: function() {
this.node.dismount();
},

renderFunc: function() {
// There are no reactive attributes on a Scene
return null;
Expand Down Expand Up @@ -77,7 +81,7 @@ FView.wrap('Scene', null, {
viewToRender = fview.blazeView.templateContentBlock;

// This materializes the Node's spacebars contents; div is never used
Blaze.render(viewToRender, document.createElement('div'), fview.blazeView);
Blaze.render(viewToRender, unusedDiv, fview.blazeView);

if (data._onRender) {
var onRender = getHelperFunc(blazeView, data._onRender);
Expand Down
Loading

0 comments on commit 632f1b6

Please sign in to comment.