INFO: Forked coz the original version was removed from npm repo
Bibimbap is a simple JavaScript immutable tree data structure supporting cursors. It is inspired by Baobab but implements only a small subset of it's features. It's much lighter and also conceptually a bit different.
It is intended to be used as a central data structure containing application state when using a library like Deku or react.
And a Bibimbap is also a light Korean dish :)
Cursors help you to write self-contained ui component that receive only their part of the global state.
- Cursors are used to navigate through the tree.
- They are also immutable, every operation returns a new cursor.
Unlike in Baobap cursors don't emit update events. The idea is to re-render everything with the next version of the state.
A todo component implemented with Deku
import { element } from "deku";
import Input from "./input.jsx";
import "./todo.css";
/**
* Todo Component
*/
export default {
render({ props: { cursor } }) {
// generates the list of items
const items = cursor.select("items").map((cursor) => {
return (
<li>
{cursor.get()} <span onClick={cursor.remover}>✖</span>
</li>
);
});
return (
<div class="todo">
<form onSubmit={addItem}>
<ul>{items}</ul>
<Input cursor={cursor} name="new-item" type="text" />
<button type="submit" disabled={!cursor.get("new-item")}>
Add
</button>
</form>
</div>
);
/**
* Action to add a item to the list
*/
function addItem(ev) {
ev.preventDefault();
cursor.push("items", cursor.get("new-item")).set("new-item", "");
}
},
};
Create new application state
var state = new Bibimbap({ some: "data" });
Create a cursor. Cursors can have an optional name.
When the name is given it returns a new cursor with that name. Otherwise it will return name name of the cursor
cursor.name('hello').name(); // hello
The default name of a cursor is its keys.
state.cursor().select('first', 'second').name(); // "first.second"
Commit the state of a cursor back to the application state. By default cursors autocommit.
Navigate down in tree
cursor.select("key");
cursor.select("first", "second");
cursor.select(["first", "second"]);
cursor.select("first").select("second");
Navigate one level up in tree
Navigate up to the root node
Almost all of the following methods accept optional key arguments. But they have no effect on the returned cursor, just on the current operation.
var nextCursor = cursor.set("first", "second", "value"); // nextCursor has still the same location
cursor.set(["first", "second"], "value"); // keys can also be in an array
Get data from the tree
cursor.get("key");
cursor.get("first", "second");
cursor.get(["first", "second"]);
Get only certain attributes, like _.pick({a: 1, b: 2, c:3}, ['a','b'])
var state = new Bibimbap({ test: { a: 1, b: 2 } });
state.cursor().only("test", ["a"]); // { a: 1 }
Map over the children
cursor.map(function (cursor, key) {
return "hello" + cursor.get();
});
Tests if data at the cursors location exists
Set a value. Autocommited unless in a .transaction
cursor.set(1); // 1
cursor.set("test", "hello"); // { test: 'hello' }
cursor.set("first", "second", "hello"); // { first: { second: 'hello} }
cursor.set(["test"], "hello"); // { test: 'hello' }
cursor.set("test", "hello").set("test2", "bla").get(); // { test: 'hello', test2: 'bla' }
Like set but returns a function that will set when called
<div onClick={cursor.setter("clicked", "yes")}>clicked: {cursor.get("clicked")}</div>
Add a new element to the end of an array
Add a new element to the start of an array
Remove the data at the cursors location
Same as .remove but returns a function
Like Object.assign
Process the state with a function. Process will always use the current data in the tree, even when called on an older cursor.
cursor.process(function inc(n) {
return n + 1;
});
Transactions allow to perform multiple actions before commiting back to the state
Start a transaction
cursor.transaction().select("example").set("bla").up().set("test", 5).commit();