balderdashy / mast Goto Github PK
View Code? Open in Web Editor NEWUI conventions built on top of Backbone.JS
License: MIT License
UI conventions built on top of Backbone.JS
License: MIT License
Then, just like Backbone.Events, take the string component and look it up. If the direct reference is attempted, it might not have been defined yet. To fix that, we'd need to manage ordering. yuck
Mast is about ready to go now. I'm ready whenever you are to spend a couple of hours making some components
Let's see how far you can get by Friday-- I'd like to see a working TODO app we can go over w/ together. Then this weekend, let's add some server-side functionality (and I can show you the single-biggest weakness in Sails right now, lack of wildcard websocket support.)
After implementing the server integration, I'd like to host the todo app and make a new git repo for the source code so we can provide it as a working example.
also need to expose this information on the serverside.
The grouping here is important-- this is asking for the list of curent active visitors for this route. Which could mean a separate page or not, who cares, it's the logical unit we're interested in, since we're probably using the client-side router.
UI extends component, but uses autorender=false
and automatically instantiates the component during Mast.raise(). It adds the .activate() method, which empties the outlet and rerenders the template for the now active UI component.
handle:
search/:query/p:page etc.
Right now, you have to pretty much hack the Mast source code to add new socket events from the server. There needs to be a file much like controller.js (AppRouter) for mapping socket events to functionality.
-OR- the socket events should propagate to the AppRouter (which would be fine as long as we remain explicit about which component is being rendered into from the AppRouter)
Why do Mast.Collection and Mast.Model have to miss out on all the fun?
At the very least, there should be a (disable-able) warning that render() and intiialize() are not a part of Mast.
The DirectoryComponent should render its branches in a fancier way to match everything else. This involves taking a fresh look, at the framework-level, at bindings vs. custom render functions, and whether we even need both.
Custom render functions can work for everything, but they must be triggerable from Component.set() -AND- Mast.Model.set(). This is because comet calls can only update a model. However this ties view functionality in at the model layer, which is a negative.
-Custom render functions work best for cases where templates+css just don't do the trick (i.e. animations unsupported by unsupported CSS3, applying styles using an algorithm, animations w/ complex timing, etc.)-
someComponent.set('someProperty','someNewValue',{
render: function($existingElement, $newElement) {
// do stuff here like fade out the old element and fade in the new
});
someComponentsModel.set('someProperty','someNewValue',{
render: function($existingElement, $newElement) {
// do stuff here like fade out the old element and fade in the new
});
Bindings are confusing, and leave open questions about how they should behave when some of the attributes are changed, and not others. Not to mention for Trees and Tables.
-Bindings work best for cases when you dont' want to force a rerender of an entire component just to change one of its model's attributes. The most efficient components have bindings for every attribute that changes in their models. This side of things could be eventually converted into a Spine-style templating scheme.-
-Finally, note that bindings were created because originally, Mast rerendered all children of a component whenever one of the parent's attributes changed. This is wildly annoying, and no longer occurs.-
bindings: {
numActive: function (newVal) {
this.$el.closest_descendant('.num-users').text(newVal);
},
numComments: function (newVal) {
this.$el.closest_descendant('.num-comments').text(newVal);
},
// This gets called whenever the depth of this component's model changes
depth: function(newDepth) {
this.setPadding(newDepth);
},
state: function (newVal) {
var arrow = this.$el.closest_descendant('div.expand-close-arrow');
// Handle basic rendering use case
this.$el.removeClass('loading expanded');
// Do animation for arrow rotation
if (newVal != "") {
this.$el.addClass(newVal);
if (newVal=="expanded" || newVal=="loading") {
arrow.addClass("expanded");
}
}
else {
arrow.removeClass('expanded');
}
}
}
So far, from the above list, the following sensible default behaviors don't exist in Mast:
Simply display:none
the Component's $el
Component.show()
Dumbly show the Component's $el
And here's what we do have:
Component.change()
If there are any bindings
for the attributes that were changed, execute them. If any or all of the attributes changed WERE NOT in bindings
, remove the Component's $el from the DOM, recompile the templates, and reinject it into its outlet.
Component.reset()
Wipe from DOM, retemplate, and then rerender the Components rows/branches.
Component.add()
Add a row/branch at the model's position in the collection
Component.remove()
Remove a row/branch by model.
Right now, the repo contains Sails+Mast, which is fine, and what we'll typically be using, but I want to support the case where someone wants JUST Mast out of the box (to use w/ PHP, rails, whatevs) The goal being that I can incorporate Mast in the new Demo Center product for Unwired, assuming that flies w/ our "legal dept.". I can't wait to see the productivity gains there.
There is logic that should be performed before every route, on on the very first route.
These should probably have a filesystem convention, whether that's to put them in a file in the /mast/routes directory or in the top level /mast dir.
Tentative spec:
Mast.beforeEachRoute(fullRoute,arguments*) {
}
Mast.beforeFirstRoute(fullRoute,arguments*) {
}
Where arguments*
are standard Backbone route arguments, e.g.: #/:arg1/:arg2
and fullRoute
is the complete route string.
Mast is about ready to go now. I'm ready whenever you are to spend a couple of hours making some components
change the compilation script to piece together any files in public/routes into the AppController instead of just absorbing public/js/controller.js.
So:
public/routes/index.js
public/routes/tableExample.js
public/routes/whateverElse.js
->
production/ _controller.js
Ideally, development mode keeps these as separate files to make it easy to see in a browser's dev console.
Allow a children-scoped selector for the events hash (i.e.
events: {
"click >a.doDelete": 'deleteThisTree'
}
Should this happen? Should it be configurable? What does that look like?
Needs public methods to add, insert and remove rows.
Needs to listen for changes to individual rows' Mast.Patterns (already done I think...)
Needs to listen for changes to the Mast.Collection itself (awareness of when models are added or removed)
Allow bindings to be specified like subcomponents:
an object definition of the form: attributeName : relativeOutletSelector
where relativeOutletSelector
refers to the DOM selector for an element inside of this.$el
. (similar to this.$()
For example:
bindings: {
someAttribute : '.some-attribute-outlet'
}
The new value of the bound attributes replaces the contents of the appropriate relative outlet.
In every case, .ejs templates for the various components should be either:
As for the JS and CSS:
we'll still need some magic to manage .css and .ejs template files, but dependencies and js loading should be done by require.js because it's a much more mature framework than anything I can write in a few hours.
In development mode, Mast.Require.js on the client-side asynchronously loads the javascript components and routes, managing dependencies all the while. CSS s are loaded asynchronously by default in browsers, and so we include them in order, from a view partial.
In production mode, require.js on the node.js side synchronously compiles the javascript components and routes into one file, app.js, on initial launch. It leverages this dependency information to compile CSS into one file as well, available from the same (smart) view partial.
NOTE: production mode should be used with care when developing client code, since any changes will result in a restart and recompilation.
Modifying a tree's collection should leave its model untouched and vice versa.
Modifying a component's subcomponents should leave its subcomponents untouched and vice versa.
Create a data structure to represent an n-branched tree (dependency list)
Use Backbone.sync semantics in combination with something like Sencha's concept of a Store.
The only really important persistence models are W3C localStorage and Socket communications. Support for standard AJAX should be overlooked for the time being.
Compile and include jQuery, jQuery UI, Backbone, and Underscore, as well as moving the generated socket.io client into the lib/client directory and minifying and compiling into _app.js in production mode.
Better yet, wrap it in a function that checks if it's already on the page before initializing. But meh that's a lot of work and easy to break still. Better to make the user manage their dependencies themselves so they're aware of what's happening.
What do you think of the models directory? Are you OK with models and collections sharing one common folder?
What about templates? Is it ok that ejs and css files share the same parent folder?
If you were going to include Mast in a simple PHP site (let's say you wanted to make a single-page application for mobile web, or something), what would the ideal structure look like? Obviously you're going to lose the advantages of the Sails integration, like auto compilation/minification, and it loading all of the necessary pieces on demand (you'll need to manually include each script file, each template CSS file, and each HTML template inside the markup)
First and foremost, decide on whether there should be a filename convention -> template label mapping for .css and .ejs files in the "templates" directory.
Next, figure out whether js Mast.Component definitions should be included here as separate files or kept separate (probably should be here).
Instead of rendering each template files as a view partial on the serverside each time a view is rendered, string all of the templates together
Also consider automatically creating a separate compilation for the css (and js?!?) files and minifying it (them?!).
Server might send any manner of message back (controllers can broadcast whatever they like). However, by convention, we use URL notation:
account/create
account/2/update
account/3/delete
..etc.
So a subscription hash (in a UserTable Component) might look like:
(trailing and leading slashes are automatically stripped)
{
'account/:id/update': 'handleUpdatedAccountEvent'
}
In the example above, the id property of the Component's model will be checked against the id passed in the message's label (:id).
To override this behavior (if you're using a non-standard REST API between your client or server, for instance) you can either check against a different model property:
{
'account/:id/update': {
id: function () { return this.get('account').id; },
action: 'handleUpdatedAccountEvent'
}
}
or just broadcast to all instances of this component:
{
'account/create': 'handleNewAccountCreatedEvent'
}
In dev mode, a non-compressed copy of Mast should be made available, with all of the dependencies, but no regular CSS or JS(controller) files should be included. CSS and JS of components should still be marshalled into one big file each.
-AND/OR-
evaluate the value of adding view variables (i.e. <%- cssfiles %> ) for this purpose.
This probably will never be an issue-- but because models are now saved in Mast.modelCache by cid, they aren't getting gc'ed() by the javascript interpreter. On desktop, this only really starts slowing things down (and then we're talking 10s of ms, never over 100) when there are more than 10,000 models. Mobile is my concern.
The best solution is probably to associate components with routes by tying components to the route they're created in at the application layer. Then, at the framework layer, free the components that were used in a route when switching routes (which removes their models from the modelCache)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.