Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,33 @@ console.log(machine.state)
// }
```

## Guard

Sometimes you may want a machine to be guarded against moving to a specific state based on some condition. You can encapsulate this logic as part of your machine. For example, say we have a door and a lock:

```js
const door = nanostate("closed", {
closed: { open: "open", light_push: "ajar" },
ajar: { close: "closed", open: "open" },
open: { close: "closed" },
});
const lock = nanostate("unlocked", {
unlocked: { lock: "locked" },
locked: { unlock: "unlocked" },
});
```

If the door is closed, the door cannot be opened unless it is unlocked. Likewise, the door cannot be lightly pushed unless it is also unlocked.

```js
door.guard("open", () => {
return lockState.state === "unlocked";
});
door.guard("light_push", () => {
return lockState.state === "unlocked";
});
```

## Nanocomponent
Usage in combination with
[nanocomponent](https://github.com/choojs/nanocomponent) to create stateful UI
Expand Down Expand Up @@ -198,6 +225,10 @@ passed.
Trigger a callback when a certain state is entered. Useful to trigger side
effects upon state change.

### `machine.onchange(cb)`
Trigger a callback when any state is entered. Useful to trigger side
effects upon state change.

### `state = machine.state`
Return the current state.

Expand Down
16 changes: 16 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ function Nanostate (initialState, transitions) {
this.state = initialState
this.submachines = {}
this._submachine = null
this.guards = {}
this.onchangecb = null;

Nanobus.call(this)
}
Expand All @@ -21,6 +23,14 @@ Nanostate.prototype = Object.create(Nanobus.prototype)

Nanostate.prototype.constructor = Nanostate

Nanostate.prototype.onchange = function(cb) {
this.onchangecb = cb;
}

Nanostate.prototype.guard = function (eventName, cb) {
this.guards[eventName] = cb
}

Nanostate.prototype.emit = function (eventName) {
var nextState = this._next(eventName)
assert.ok(nextState, 'nanostate.emit: invalid transition' + this.state + '->' + eventName)
Expand All @@ -29,7 +39,13 @@ Nanostate.prototype.emit = function (eventName) {
this._unregister()
}

if (this.guards[eventName] && (this.guards[eventName]() === false)) {
return
}
this.state = nextState
if (this.onchangecb !== null && typeof this.onchangecb === 'function') {
this.onchangecb(nextState);
}
Nanobus.prototype.emit.call(this, nextState)
}

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "nanostate",
"description": "Small Finite State Machine implementation",
"repository": "choojs/nanostate",
"version": "1.2.2",
"version": "1.2.3",
"scripts": {
"start": "node .",
"lint": "standard",
Expand Down
16 changes: 16 additions & 0 deletions test/event.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
var tape = require("tape");

var nanostate = require("../");

tape("event - any change", (test) => {
test.plan(1);
var machine = nanostate("green", {
green: { timer: "yellow" },
yellow: { timer: "red" },
red: { timer: "green" },
});
machine.onchange((nextState) => {
test.equal(nextState, "yellow");
});
machine.emit("timer");
});
7 changes: 4 additions & 3 deletions test/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require('./nanostate')
require('./parallel')
require('./hierarchical')
require("./nanostate");
require("./parallel");
require("./hierarchical");
require("./event");