GOO
Goo.js is a small framework made to jumpstart projects by solving common web application challenges. The default goo package includes state management, layout and routing functionality. Each of these parts are separate stand-alone modules that are combined together into the final goo function. All modules have a common .use
function that accepts goo's configuration objects called blobs.
Top level api
// creates the app; // target: DOM node // window: window object can be specified if needed // defines the layout of your app // init: function that returns an element builder function [() => (state) => element]; // route: string pattern to match paths (same as in express) // init: function that returns an element builder function [(routeParams) => (state) => element] // sets the state from which layout is createdapp; // state: an object to replace the current stateapp; // updater: function that returns the new state [(currentState) => ... newState] // returns a copy of the current stateapp; // changes the path and renders layout from the new routeapp; // path: string of the new pathname // params: object to be passed to the route handler // renders layout from the new routeapp; // path: string of the requested route's path // params: object to be passed to the route handler // executes an action on the stateapp; // type: string of the action type // params: arguments given to the action handlerapp; // action: function that returns the new state [(currentState) => ... newState] // adds a blob to the app and sends it to all modulesapp; // blob: the blob to be added // triggers a rerender from current state (for use when changes are not represented in the state)app; // undoes the last action (or setState call)app; // redoes the previous actionapp;
Syntax
text element
let elem = textContent; // textContent: content of the textNode
let elem = 'Hello World!'; // Hello World!
tag element
let elem = tagName attributes children; // tagName: string representing the dom tag's name // attributes: object containing all attributes of the element // children: array of child elements
let elem = 'div'; // <div></div> let elem = 'div' id: 'banner'; // <div id="banner"></div> let elem = 'div' {} 'Hello World!'; // <div>Hello World!</div> let elem = 'div#banner'; // <div id="banner"></div> let elem = 'div.nav'; // <div class="nav"></div> let elem = 'div | height: 10px;'; // <div style="height: 10px;"></div> let elem = 'div#banner.nav.hidden | font-size: 20px;'; // <div id="banner" class="nav hidden" style="font-size: 20px;"></div>"
component element
let elem = component props children; // component: function which returns an element [(props) => element] // props: object containing all props for the component // children: array of child elements
let 'div' style: `color: ` children; let elem = component color: 'red' 'content' // <div style="color: red;">content</div>
Example
let app = ; app; let 'li.fruit' {} type ; ;
orange apple pear
Blobs
Goo and its modules all have a use
function which takes a single object as argument called a blob. Blobs are very powerful since they are understood by all modules and allow access to deeper layers than the top level api.
A blob can have as many unique keys as necessary. Each key will be tested on each module to determine if that module supports it. The value assigned to each key can be a single object, or an array of objects (ex. adding multiple actions at once). This library is purposefully built to handle the addition of blobs in any order and at any time in an application's lifecycle.
Here is an example of a blob that adds two watchers and adds a route.
app;
Named blobs are an extension of regular blobs that ensure that the blob is only ever used once in a single app instance. This can be done by simply adding a "name" key to a blob and using it as before.
let myPlugin = name: 'myPluginName' middleware: myMiddlware app;app; // will not add the middleware again
Here is the list of the recognized blob keys and the modules that consume them.
module | recognized keys |
---|---|
goo-state | name , action , watcher , middleware |
goo-router | name , route , base |
goo-dom | name , target , builder , state , draw , update , build , prebuild , postbuild |
action
let action = type target handler // type: string which names the action // target: array to restrict the scope of an action to a specific "address" in the state // OR function which returns the action's target [(state, params) => ... target] // handler: function that makes the change on the target [(target, params) => modifiedTarget]
app; let action = type: 'REMOVE_HOBBY' target: 'hobbies' { hobbies; return hobbies; }; app; app; app; // {name: 'John', hobbies: ['gardening']}
watcher
let watcher = watcher // watcher: function that gets called after each state change [(state, actionType, params) => ...]
Watchers cannot modify the state since they are given a copy, but they can issue more actions.
let { console;}; app;
middleware
let middleware = middleware // middleware: function that is given control of an action before it is executed // [(next, state, actionType, params) => ... next(state[, actionType[, params]])]
This syntax allows middlware to be asynchronous.
If next
is called with parameters, they will override the ones issued by the act call.
let { if paramstest console; ; else ; }; app;
route
let route = path callback // path: string pattern to match paths (using express' syntax) // callback: function that is called with the route params as argument [(routeParams) => ...]
let route = path: '/user/:uid/profile' { // ... }; app;
base
let base = base // base: string specifying the base url of the page(s)
let base = '/subdir/myapp'; app; app; // navigates to '/subdir/myapp/users' // matches routes for '/users'
target
let target = target // target: dom node in which to draw the app
let target = document; app;
builder
let builder = builder // builder: function which creates an element out of state [(state) => element]
let 'div | background-color: red;' {} 'span.title' {} statetitle ; app;
state
let state = state // state: object to update ONLY the layout's state
It is not recommended to use this key. It will ONLY change the state of the DOM module.
let app = ; app; ;// <span>originalState</span> app;// <span>newState</span> app;// 'originalState' app;// <span>originalState</span>
draw
let draw = draw // draw: function that handles the initial drawing to a new target [(target, vdom) => ... vdom]
Using this blob key will override the default draw function.
let { targetinnerHTML = ; return vdom;}; app
update
let update = update // update: function that updates the target with new vdom [(target, vdom, currentVdom) => ... vdom]
Using this blob key will override the default update function.
By overriding both the draw and the update functions it is possible to "render" to any target in any way.
Here is an example of an app that renders to a string instead of a DOM node.
let realTarget = ''; let { realTarget = ; return vdom;}; app
build
let build = build // build: function that creates VDOM out of the return value of an element [(element) => vdom]
let element = let vdom = 'div#title' {} tagName: 'div' 'Hello World!' attributes: id: 'title'); children: text: 'Hello World!' ;
prebuild
let prebuild = prebuild // prebuild: function to manipulate elements before they are passed to build [(element) => ... element]
Here is an example which wraps the app in a div.
let 'div | width: 100vw;' {} original ; app;
postbuild
let postbuild = postbuild // postbuild: function to manipulate the vdom created by the build function [(vdom) => ... vdom]