npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

harel

v0.0.1

Published

Harel Statecharts for javascript

Downloads

3

Readme

Harel Statecharts

Harel Statecharts in javascript for declarative UI

Harel statecharts are a declarative way to describe user interface behavior. They're fun to make and easy to maintain. This library has a minimalistic implementation with robust support for nested and parallel charts.

Usage

var Harel = require('harel')

// A basic countdown timer UI
var timerChart1 = Harel.create({
  states: ['running', 'paused', 'reset', 'finished'],
  events: {
    START: [
      ['paused', 'running'],
      ['reset', 'running']
    ],
    PAUSE: ['running', 'paused'],
    RESET: [
      ['running', 'reset'],
      ['finished', 'reset'],
      ['paused', 'reset']
    ],
    DONE: ['running', 'finished']
  },
  initial: {reset: true}
})

var c2 = timerChart.event('START') // c2.states -> {running: true}
var c3 = c2.event('PAUSE') // c3.states -> {paused: true}
// alternative syntax:
var c4 = Harel.event('RESET', c3) // c4.states -> {reset: true}

They support parallel states -- simply set multiple states to true in the initial property.

API

var Harel = require('harel')

Harel.create(options)

Create a new chart instance with some options. You can set these props in the options:

  • states: array of state names (strings)
  • events: object of event names mapped to [source, dest] state name pairs.
  • initial: object of initial active states, like {running: true, valid: true}

Each value in events can be a single pair like ['running', 'paused'], where the event can transition from "running" to "paused". Or it can be an array of pairs (like [['x', 'y'], ['a', 'b']])

Returns a chart instance with these props:

  • .event(eventName) -- fire an event and return a new chart (see below)
  • .states -- object of active states. Each key is a state name and each val a bool.

It will throw an error if a state is unreachable, if a state name is invalid, or if any state transition is ambiguous.

If any state is inaccessible or if any transition is ambiguous, then an error will be thrown immediately on creation.

Harel.event(eventName, chart), chart.event(eventName)

Trigger a state transition using an event name. You can either call this as a method on a chart instance or as a function with a chart instance as the second argument.

This returns a new chart instance with a new .states property.

It will throw an error if the event is invalid.

Nested Charts

One of the goals of this module is to have robust support for nested charts.

Initializing a nested chart and entering its initial state

Use the .where property in a statechart to have a nested chart. When you transition into that chart, you can enter its initial state.

const c1 = Chart.create({
  states: ['s1', 'nested'],
  events: { PUSH: ['s1', 'nested.initial'] },
  initial: {s1: true},
  where: {
    nested: {
      initial: {a1: true},
      states: ['a1']
    }
  }
})
const c2 = c1.event('PUSH')
t.deepEqual(c2.states, {nested: {a1: true}})

If you initialize a parent chart to have a nested chart active, then it will automatically set the nested chart's initial state:

const c1 = Chart.create({
  states: ['nested'],
  initial: {nested: true},
  where: {
    nested: {
      initial: {a1: true},
      states: ['a1']
    }
  }
})
t.deepEqual(c2.states, {nested: {a1: true}})

Notice in the above that instead of setting .states to {nested: true}, it set it to {nested: {a1: true}} using nested's initial state.

Transition into a specific state

Instead of transitioning into a nested chart's initial state, you can transition into a specific state:

const c1 = Chart.create({
  states: ['s1', 'nested'],
  events: { PUSH: ['s1', 'nested.b1'] },
  initial: {s1: true},
  where: {
    nested: {
      initial: {a1: true},
      states: ['a1', 'b1']
    }
  }
})
const c2 = c1.event('PUSH')
t.deepEqual(c2.states, {nested: {a1: true, b1: true}})

Here we specifically transition into c1.b1 instead of into its initial state. Notice that it keeps the initial state active.

Transitioning between nested charts

You can transition from a specific state within one nested chart to a specific or initial state in another nested chart.

const c1 = Chart.create({
  states: ['n1', 'n2'],
  events: { JUMP: ['n1.a1', 'n2.initial'] },
  initial: {n1: true},
  where: {
    n1: {
      initial: {a1: true},
      states: ['a1']
    },
    n2: {
      initial: {a1: true},
      states: ['a1']
    }
  }
})
const c2 = c1.event('JUMP')
t.deepEqual(c2.states, {n2: {a1: true}})

Transitioning within a nested chart

To fire an event in a nested chart, prepend the nested chart name before the event name:

const c1 = Chart.create({
  states: ['n1'],
  initial: {n1: true},
  where: {
    n1: {
      initial: {a1: true},
      states: ['a1', 'b1'],
      events: {JUMP: ['a1', 'b1']}
    }
  }
})
const c2 = c1.event('n1.JUMP')
t.deepEqual(c2.states, {n1: {b1: true}})

You can also define an event in a parent chart that transitions a nested chart, if you'd like.

const c1 = Chart.create({
  states: ['n1'],
  events: { JUMP: ['n1.a1', 'n1.b1'] },
  initial: {n1: true},
  where: {
    n1: {
      initial: {a1: true},
      states: ['a1', 'b1']
    }
  }
})
const c2 = c1.event('JUMP')
t.deepEqual(c2.states, {n1: {b1: true}})

Transitioning into a nested chart's history

Nested charts have a special state called the "history" state that can be used to restore whatever states were active last when transitioning into the chart.

For example, say you have a simple nested chart with its initial state active, you transition to another state in that chart, and then you exit. Finally, you re-enter the nested state using the ENTER transition, which will enters n1's "history" state. This means that n1 will activate its previously active states, rather than its initial states.

const c1 = Chart.create({
  states: ['n1', 's1'],
  events: {
    JUMP: ['n1.a1', 'n1.b1'],
    EXIT: ['n1', 's1'],
    ENTER: ['s1', 'n1.history']
  },
  initial: {n1: true},
  where: {
    n1: {
      initial: {a1: true},
      states: ['a1', 'b1']
    }
  }
})

t.deepEqual(c1.states, {n1: {a1: true}})
const c2 = c1.event('JUMP')
t.deepEqual(c2.states, {n1: {b1: true}})
const c3 = c2.event('EXIT')
t.deepEqual(c3.states, {s1: true})
const c4 = c3.event('ENTER')
t.deepEqual(c4.states, {n1: {b1: true}})

Exiting any state from a nested chart

You can exit from all/any states in a nested chart by not specifying a specific state to transition out of:

const c1 = Chart.create({
  states: ['n1', 's1'],
  events: {
    EXIT: ['n1', 's1'],
  },
  initial: {n1: true},
  where: {
    n1: {
      initial: {a1: true},
      states: ['a1']
    }
  }
})

const c2 = c1.event('EXIT') // this will exit from *any* state in n1
t.deepEqual(c2.states, {s1: true})

Looping on a nested chart

You can transition in a nested chart that goes from any state to a specific state within the same chart.

Transitioning between deeply nested charts

Any of the above transitions among nested charts is equally applicable to deeply nested charts. To fire an event that lives in a deeply nested chart, you can run something like chart.event('s1.s2.s3.EVENT_NAME').

To define an event that transitions a deeply nested chart, you can give an event transition pair such as ['s1.s2.s3.a1', 's1.s4.s5.b1'].

Install

With npm installed, run

$ npm install harel

Acknowledgments

Thanks to davidkpiano for giving a talk about state charts.

See Also

License

MIT