meteor-famous-views
meteor-famous-views copied to clipboard
API for adding custom components.
If you could abstract this away with some new API so that someone making a plugin for fview doesn't have to worry if it works just in app mode or not.
I imagine that making a plugin should ideally be super simple. For example, famous-views could require components to be RenderNodes at the top level (I think you mentioned that they are in another conversation) which are the basis for constructing the render trees, then devs could do something like the following with their custom components:
// if their component is, extends, or imitates a RenderNode:
FView.registerComponent(MyComponent /*a class*/, "FviewComponentName" /*template name*/, true /*accepts children?*/);
// if their component is not, does not extend, or does not imitate, a RenderNode:
FView.registerComponent({
component: MyComponentThatDoesntHaveARenderNodeAPI, // whatever it is, who knows.
create: function(component, templateOptions) { // the create function describes how to set up a RenderNode containing the desired 3rd party component.
return (new RenderNode).add((new componentClass()).foo.bar.modifier); // somewhere in the component is the base modifier of the component, for example, who knows.
// create must always return a RenderNode (or something that extends or imitates it).
}
}, "FviewComponentName", true);
then in the template
+FviewComponentName
+ChildComponent
If someone's component isn't a RenderNode or isn't extending a RenderNode at the top level, they could easily adapt by using an Object literal to specify the component and create function that must return a RenderNode.
If the user wants add sub components to his component, f.e.
+MyComponent(foo="...")
+Modifier
+Surface(prop="...")
+SomeOtherComponent(blah="...")
then that's no problem. Since the FView.registerComponent method is used to specify a RenderNode or how to create a RenderNode, then by default FView can just call RenderNode.add
for each of the sub components. Ultimately, it will be vanilla Famo.us under the hood determining if those calls to .add
are valid (i.e. checking if you have a Modifier or Surface (or things that imitate them) inside the RenderNode). But just in case the component has specific logic for adding things, the object literal could also accept a add
property that specifies a function describing how to add things to the component, in which case FView behind the scenes calls this alternate add
method instead of the RenderNode's add method when all sub components are instantiated. For example, extending the previous example:
// if their component is not, does not extend, or does not imitate, a RenderNode:
FView.registerComponent({
component: MyComponentThatDoesntHaveARenderNodeAPI,
create: function(component, templateOptions) {
return (new RenderNode).add((new component()).foo.bar.modifier);
},
add: function(myComponentInstance, children) { // subComponents is an array of RenderNodes (or objects with the RenderNode API).
children.forEach(function(child) {
// f.e. the dev's component might only accept Surfaces with it's API, who knows:
if (child._object instanceof Surface) {
myComponentInstance.foo.bar.addChild(child._object);
}
// non-Surface children are simply ignored by this function, and should ultimately be garabage collected (when MixedMode fixes the currently infamous memory leak).
});
}
}, "FviewComponentName", true)
Lastly, as you see in the previous jade snippet, the custom component accepts attributes (e.g. the foo="..."
). That is what the create
method's templateOptions
parameter is for. The dev can do what he wants with them, for example, inside the create function:
return (new RenderNode).add((new component( templateOptions )).foo.bar.modifier);
where templateOptions
is an object literal containing each attribute, e.g. { foo: "..." }
. It could be possible to manipulate templateOptions
with other FView methods before passing them into the component constructor, etc, etc, whatever the dev desires.
With just these three properties in the object literal passed into registerComponent
, we pretty much have everything we need to define custom components with a single, easy-to-use, consistent API, and users don't need to worry about app mode or not, their components just work.
This is all just an idea, but something this simple (complexity behind the scenes of famous-views) would be nice.
Hey, saw the comment first. My reply from there:
Hey. Unfortunately we can't abstract away things like whether a plugin works in appMode or not. If not in appMode and there's no mainCtx, what does fview-lagometer attach to? It's meant to just work out the box. Obviously for multiple contexts, yeah, we'd like to be able to do a +Lagometer in the markup, that's the "doesn't work for now..." part in the error :>
Otherwise, I think everything you described already exists? See http://famous-views.meteor.com/views/README. Don't worry about the "view" name :) You can override the
create()
function too, that's not in the docs but I'll add it now: https://github.com/gadicc/meteor-famous-views/blob/master/lib/views.js#L22.
Let's continue the discussion here. There is also other stuff we could add too, like some hooks for plugins to all use at the same time, and maybe custom loggers which show the plug name, etc.
@gadicc
Re: registering views
I just read http://famous-views.meteor.com/views/README. Doh! I should've looked first, but yeah, basically that. We were thinking the same thing. xD Let me actually try it before giving more ideas so they're more meaningful. :>
Re: appMode and stuff
Unfortunately we can't abstract away things like whether a plugin works in appMode or not
Why not? Just create a convention like famous-angular does. Their convention is that there's only one way to make a context: <fa-app>
. If you want a "full page" app, then you just put an <fa-app>
inside the <body>
, and make it take up the full size of the body. Every <fa-app>
has it's own div with a context on it, even if it's the only child in the body template.
You could do the same thing with famous-views by requiring a +famousContext
and (IMHO) deprecating (or better yet, entirely dropping) the old app mode. It makes sense for widely established libraries to maintain backwards compatibility with their stable APIs, but famous-views is brand new, so there's no need to be backwards compatible with APIs that are been stepping stones towards what a stable API should be.
Following this convention, fview components would always need to be inside a famousContext:
+famousContext
+Lagometer(size="[200,200]")
The docs for fview-lagometer can explain that a Modifier gets created, with it's size set to [200,200], and inside that is the lagometer.
If you just drop "app mode", and make +famousContext
a requirement, like what famous-angular does with <fa-app>
, everything will be easier to understand and work with. Using the famousContext convention would be super beneficial for the API: it'd make fview-plugin component registration clean, precise, and universal; users won't have ifs, ands, or buts about how to use famous-views; and developers won't have concerns or confusion about which "mode" to make their fview-plugin will work with (one? the other? both?). Devs will have a single way to create plugins that work in a single environment (inside +famousContext
), concisely and precisely. Sure, you might have to make some code changes, but it's worth it. It will avoid fragmentation where some plugins work on ly in app mode, other only inside famousContext, etc. That's friction that no end user should have to deal with.
Famous-views isn't widely adopted yet (all the plugins @PEM-- and you have recently added are super awesome, but they are brand new and testing the waters of the API), so people should expect changes to the maturing API before it becomes completely stable and out of beta state.
Anyways, whatever you decide to do I will gladly live with. :)
Adding to the idea of reducing friction for end users, I think that all the default Famo.us views shouldn't need to be registered manually by the end user. Users should be able to do +Scrollview
without having register it with FView.registerView('Scrollview', famous.views.Scrollview);
. It's understandable that this should be required for custom views that aren't shipped with Famo.us, but supporting all the default Famo.us views out of the box would expected.
Those are my humble opinions on reducing friction for developing fview-plugins, and developing with fview.
In https://github.com/gadicc/meteor-famous-views/issues/113#issuecomment-64985025 you said
Also since mixed mode gets rid of modifiers, definitely makes sense to drop
registerModifier
andregisterView
for justregister
).
A single method makes sense. Ultimately it doesn't matter what Famo.us decides can be inside a RenderNode (no more Modifiers, now Surfaces and Planes among other things). The overarching idea is that we have a tree of RenderNodes, so a nice convention would be to require myViewClass
in FView.registerView('MyView', myViewClass, {...})
to be a RenderNode, but if it's not a RenderNode then require the create
method to return a RenderNode. (I'm repeating this part of my original comment a little). This will make development of plugins precise, because after all, we're creating render trees ultimately when we make a +component
.
If this._view
is a RenderNode, then
// If exists, called to instantiate the famo.us class
create: function(options) {
return new this._view.constructor(options);
},
would work, and would be useful for special initialization steps. If this._view
isn't a RenderNode, then you'd need to do something like
// If exists, called to instantiate the famo.us class
create: function(options) {
return (new RenderNode).add(new this._view.constructor(options));
},
If your plugin isn't going to have anything to do with the explicit creation of a sub-rendertree (f.e. famousEach
doesn't explicitly create a node, it just loops and adds multiple of them) then there can be another method for that, which brings me to the following idea:
Idea: perhaps have two methods: registerNode
and registerHelper
. As the names imply, registerNode
is for explicitly registering a RenderNode that has whatever sub-rendertree you wish, and registerHelper
is for when you want to register a +component
that will not create a sub-tree, but will receive references to all it's children or it's template so it can manipulate them somehow (e.g. registerHelper('famousEach', ...)
.
Relating to your comment at https://github.com/gadicc/meteor-famous-views/issues/113#issuecomment-64985025, maybe the term then could be "nodes"? Ultimately that's what a part of a render tree boils down to: a (render) node.
(Note: I put my original comment from https://github.com/gadicc/fview-lagometer/commit/c2a4e0dd05399e0530f2ca9dc6d5394741acd5ce#commitcomment-8754938 at the top of this thread, so it's easier to reference.)
Does my registerNode
vs registerHelper
idea make sense? I'm more interested in registerNode
. In fact, I'd be fine with just having that single method, as I could return a RenderNode with all sorts of functionality attached to it (for example something like mjn:fview-animate). RenderNodes don't actually have to contain subtrees, they could just be an empty RenderNode (without a ~~Modifier~~, Surface, or etc in it). F.e., if I registerNode('MyComponent', MyCompClass, ...)
and MyCompClass
is an empty RenderNode that does nothing, or the create
method returns an empty RenderNode that does nothing, then the following two examples would be equivalent in outcome to the end user of the app:
+Modifier(...)
+MyComponent
+MyComponent
+MyComponent
+MyComponent
+MyComponent
+Surface(...)
behaves exactly the same as
+Modifier(...)
+Surface(...)
But I could see registerHelper
being useful for taking advantage of templating features like how +famousEach
does, so, wait, yeah, a method other than registerNode
would be needed. I really need to start playing with registering my components as fview-plugins. I have a blocker right now in that I need to make a dist
of my project that contains compiled ES5 which would then make it easier to start wrapping my stuff for meteor, then start putting things in fviews.
Haha wow you wrote a lot here :)
Ok so appMode isn't a famous-views thing, it's a famous thing. To be clear, we are now promoting the convention now of explicitly putting a +famousContext
in body, with id="mainCtx"
being a special attribute that turns on appMode (see QuickStart, API docs, and draft university pages, etc).
appMode means that we have a single "main" context which occupies the full viewport and handles everything. I don't think it's reasonable to ask the user to set up all this CSS on their own (equivalent of .famous-root
class on html
,body
, i.e. full width/height, overflow:hidden, no margins, no padding and preserve-3d. I'd like users to know that if they set up a main context, that's all they have to do to have an app work as a full page app without any additional work.
The idea with lagometer is that the user can install the plugin and have it work out the box. They just meteor add gadicohen:fview-lagometer
and can use it straight away without adding extra mark up. And remove it at any time without extra markup. If it detects a main context, it can just add itself automatically. If there's no main context, then it's not clear where it should go, and will require the user to put +Lagometer
somewhere (which as mentioned, is not implemented yet but is planned -- a little more complicated since fview-lagometer is never deployed). This is what I mean when you can't get rid of checking whether appMode is on or not... if we check, we can make it super easy for the dev to integrate if they're doing a full page app, and a little extra work if they're not. I really think this is a winning pattern for ease-of-use. Especially when potentially lots of plugins are involved. It's a chore to add/remove extra markup in the page for every install/uninstall.
Will reply to the "node" thing in the other thread. What you say about renderNodes makes sense, but as I said, I'd like to save these kinds of changes for when mixed mode comes out. The current way of doing things makes sense for the current version of famo.us and is quite commonly used... I'd like to save all the big changes for a single big version upgrade. It's not only plugin authors that are using the API, but all our users too. As it is, with our very small changes until now but fast development pace, we get some issues with that.
Last thing, still looking very forward to seeing fview versions of your components :) When can we expect them? :)
Yeah, I can't wait to try it!! It's hard to say how long. I'm just learning a lot of jspm experimenting with the architecture of my site. I want to try compiling things for "production", both my site and my components. At that point I'll try getting them into fviews. Can't wait. :D
I forgot about the mainCtx
context thing. I remember you mentioning it.
appMode isn't a famous-views thing, it's a famous thing
I know it's how vanilla famous works, but famous-angular gets rid of it, so one must use fa-app
anytime they want a context, and that context never takes over the body. I actually think this is very nice, and better than Famo.us' default assumption that Famo.us should just take over the whole page, when in fact it's not necessary. For example, look at the Famo.us University Angular tutorials. The contexts aren't on the body, but the demos appear to be "full page" as far as the end user is concerned.
I don't think it's reasonable to ask the user to set up all this CSS on their own (equivalent of .famous-root class on html,body, i.e. full width/height, overflow:hidden, no margins, no padding and preserve-3d.
Indeed, it is totally unreasonable. Famous-angular is solving this by applying the CSS to the div containers that it creates. The container created by <fa-app>
has a default width and height of 100%, so when it's in the body, it takes up the whole space of the body, thus it's a whole page app without putting the context on the <body>
. So basically, if using the same method as famous-angular, then "all they have to do to have an app work as a full page app without any additional work" is:
body
+famousContext
and it just fills the body. If a dev doesn't want it to be full page, then the dev could put it inside some element, or just set it's size:
body
+famousContext(width="200", height="200")
and `id="mainCtx"' wouldn't be required and would reduce some usage friction.
If it detects a main context, it can just add itself automatically
Aah, I see what you've done. Nice idea! What about not worrying about there being a mainContext anywhere in the registration process, then, just having the plugin create a new context entirely and making sure that this new context is on top of the other elements on the page (including other contexts) with zindex:1000000
, then putting the lagometer in there? Then it wouldn't matter any contexts exist at all. Since all contexts run on a single Engine, this should work. So you could still do this even if there's only a single type of context (no mainCtx).
I'm not entirely sure, but it seems like what gets returned from the create
method will be set()
into the MeteorFamousView
render node. Theoretically, if a dev keeps up with Famo.us as it changes to mixed mode, then the dev can return anything that's acceptable in a RenderNode and it should be fine for when Mixed Mode comes out, assuming you're not creating any Modifers behind the scenes, but this might be limiting since the new RenderNodes can have more than one thing in them last I saw. If you force the dev to return a RenderNode from create
, then the dev could put whatever he wants into the RenderNode before returning it, so famous-views wouldn't have to worry about Mixed Mode, only the dev would before returning the eventual RenderNode. I think the RenderNode.add and .set API will remain the same, it's just what you put in the RenderNodes that makes the difference. But yeah, so if an official convention is to return a RenderNode from create
(or enforce it by throwing an error if it's not a RenderNode), then it would be up to devs to keep up with Famo.us, as far as the component registration process goes.
@gadicc
Last thing, still looking very forward to seeing fview versions of your components :) When can we expect them? :)
Well, I've recently finally added my components to npmjs.org: https://www.npmjs.com/package/infamous
I've gotta make it compatible with ES5 (a build step with traceur that puts it in a dist folder as ES5? Or perhaps load es6-module-loader and traceur? Any ideas?), then I should be ready to start trying to get them into famous-views. :)
@PEM-- @gadicc Happy new year guys! I've finally gotten my UI components up on GitHub and consumable by anyone (hopefully) even if they aren't using ES6 and no matter what module system they use. With what little free time I have, I'd now like to focus on pumping out components and updating the README with usage for each component. It'd be nice to get the components into famous-views now too! Alright, off I go to fix my PushMenuLayout so that the options can be passed into the constructor instead of hard coded.
Just starred it :star: :smile: Sounds like a new set of famous-views plugins are on their way!
Almost there guys!!! I made it consumable by browserify, and webpack. I'm adding a jspm worflow, and I might also add a bower+requirejs workflow, along witha global build. After having handled consumability of my lib, then I'd like to focus making components and/or getting it running in meteor. Slowly but surely. xD @PEM-- @gadicc
Are you guys on http://gitter.im yet?
I opened http://gitter.im/trusktr/famous just a few moments ago, for the time being until if/when famous makes one. No one's there yet. xD
@gadicc @PEM-- Alright, I'm (finally) going into Famo.us today to start making more things. I'd like to think about model-view binding. I'd like for it to be doable programmatically with my components, but also with famous-views. @kof made cyclic, and with famous-views it relies on Meteor's stuff. Mind taking a look at infamous, try installing it with one of those workflows in the README, and possibly thinking of a direction we could take so that as I make components, they are easy to use with famous-views? I can add a fview-infamous repo to the infamous org. Yeah, I think I'll do that. :D
@gadicc @PEM-- Ok, I added you guys to https://github.com/infamous/meteor. I also made https://atmospherejs.com/infamous, but on accident it's a developer account, so I'm trying to see if I can switch it an org. ALso @gadicc, https://atmospherejs.com/famous seems to be taken, but unused. We might be able to ask if we can take it over, then that can be the official famous integration. meteor install famous:views
, and @mcbain could put mjn:famous
at famous:src
or something.
@gadicc @PEM-- I changed the name to meteor-famous-views
. I think that is better to show the relation. :D I want to get started with it real soon. I suppose I should first make the wrapper for infamous on Atmospherejs first. Maybe I can copy what @mcbain did with mjn:famous? Then make the fviews plugin after that, and perhaps call it infamous:fviews
? What do you guys think? Is there an install hook I can take advantage of to convert ES6 to ES5? Or should I just make a repo specifically for the atmosphere version that contains the pre-built ES5?
@gadicc @PEM-- Wait! I changed it one last time. fview-infamous
. Muuuuch better.
Yeah, much better :+1:
Hey guys, sorry, I'm back from Thailand and have my laptop again but I still have a bit of travel ahead of me. I promise this will get my full attention as soon as I'm able, although realistically that might only be next week. Appreciate the patience and hope to come back from this holiday with a lot of energy :)
sweeeeeet. Have fun!!
@trusktr, here we go, got you started :)
https://github.com/gadicc/fview-infamous
I couldn't fork and PR since infamous/fview-infamous was empty. Maybe delete that and fork mine, unless you already have something going on there.
For any discussion, let's open another issue (maybe on that repo). I'm probably not around for the rest of today though.
Just for mjn:famous
for now.
Oops, I previously meant to add you to the org. Invite sent!
Thanks for doing this!! This is awesome, a great starting point for me to learn from. Thank you! :D
How would I dev with this locally? Is there something similar to npm link
for meteor?
Cool. Pushed to infamous org repo and deleted the gadicc one. Added CONTRIBUTING.md - hope it's all clear and works like I'm expecting :> Let me know if you run into any trouble. I just did the square demo from the snippets.
Oh also trusktr.io is down and the whois says pendingDelete?
Sweet, thanks a ton for this! It's a lot easier than I thought to get deving. I've added two methods to PushMenuLayout, setMenu and setContent, so now I'd like to figure out how to make this work:
+PushMenuLayout(...)
+menu
+AnythingYouWantHere
+AnythingYouWantHere
+content
+AnythingYouWantHere
so that menu
and content
are like an interface to both add methods. Oh man, can't wait to see it working!! :D
I had to renew trusktr.io (for $200 because that's what happens when you fail to renew a .io domain more than 5 days prior to the expiration date), now I gotta set up the new https, so it's a red https until this weekend. :)
Damn thieves all over this internet! :(
Yeah it's super easy to integrate vanilla famo.us stuff, the hardest part is actually just getting it to work with both mjn:famous and raix:famono. If it was just famono, you could include the upstream lib straight from bower/github. Although either way, if you wrap something in the package you have to bundle the wrapped component code in the package too... there are ways around it but they're not so convenient for the dev. Anyways :) Most important thing is how the wrapper itself works, those other issues will all get solved eventually.
Re PushMenuLayout, maybe you could do something similar to the approach I used for HeaderFooterLayout (docs, wrapper):
+PushMenulayout
+SequentialLayout target="menu" direction="Y"
+Surface etc
+Surface etc
+Modifier target="content"
+Surface etc
and then
FView.registerView('PushMenuLayout', Infamous.PushMenuLayout, {
add: function(child_fview, child_options) {
var target = child_options.target;
if (!target)
throw new Error('PushMenuLayout children must specify target="menu/content"');
var method = 'set' + target.substr(0,1).toUpperCase() + target.substr(1);
this.view[method](child_fview);
}
});
Hi @gadicc! I'm trying to get the fview-infamous demo working, but no luck. I've updated build.sh to this:
#!/bin/sh
mkdir build
# TODO, check for
#npm install -g browserify 6to5
npm install infamous army-knife
mv node_modules/ build/
It didn't have mv node_modules/ build/
before. So after node_modules are in build, then I
cd demo
meteor
which launches it on port 3000, then in the browser I see this:
Any ideas?
Side question: When I publish it to Atmosphere, how do the steps of build.sh get carried out? Do we need to npm install
before publishing to Atmosphere so the package contains all the dependencies?
Oh oops, yeah, that should be in the build in the directory.
The problem is that you have a new dep in the Infamous code, var simpleExtend = require("./utils").simpleExtend;
- where is it coming from? Don't see it in the repo either. You need to include the source code in the package.
Yeah, build.sh does everything in advance and then the actual package is published with all the necessary files. Meteor does actually allow "real" npm dependencies, but for node/server code... I'm not really sure how to use npm for client code. Also, Meteor are currently working on native 6to5 support, which will also ease this whole process for us in the future.