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

snabbdom-statechart-components

v0.0.7

Published

Fractal UI components using snabbdom, Harel statecharts, and event emitters

Downloads

16

Readme

harel-components

Fractal UI components using snabbdom, Harel statecharts, and event emitters

A fun way to create UI components for the web. Features:

  • Use Statecharts to manage your UI behavior
  • Efficient virtual-dom subtree rendering and patching with snabbdom
  • Build small UI blocks in small reusable, composable, aggregatable chunks

Usage

Every component has a statechart, a set of states, a store, a set of actions, and a view.

  • The statechart describes the mode in which the component is currently in (opened, hidden, loading, etc)
  • The store is a set of internal data, usually an object
  • The actions are store updater functions that run when the statechart transitions.
  • The view renders the component as HTML. It updates the html efficiently every time the statechart transitions.

A component is an object with a certain set of keys. You can instantiate a component with the Component function.

The following example is a countdown timer. See the /examples directory to see the source code, as well as a timer-list-component that dynamically aggregates many countdown timers.

var Component = require('snabbdom-statechart-components')

// A countdown timer component

// Define a component, which is just an object with certain keys
// Here we can configure our component with a custom duration

function Timer (duration) {
  return {
    trace: true,
    states: ['running', 'paused', 'reset', 'finished'],
    events: {
      START: [
        ['paused', 'running'],
        ['reset', 'running']
      ],
      STOP: ['running', 'paused'],
      RESTART: [
        ['finished', 'reset'],
        ['running', 'reset'],
        ['paused', 'reset']
      ],
      SET_DUR: ['reset', 'reset'],
      DONE: ['running', 'finished'],
      TICK: ['running', 'running']
    },
    initialStates: {reset: true},
    actions: {
      SET_DUR: function (timer, val) {
        timer.store.ms = val
        timer.store.duration = val
        return timer.store
      },
      START: function (timer) {
        tick(timer)
        return timer.store
      },
      RESTART: function (timer) {
        timer.store.ms = timer.store.duration
        return timer.store
      },
      TICK: function (timer) {
        timer.store.ms = timer.store.ms - 10
        return timer.store
      },
      DONE: function (timer) {
        timer.store.ms = 0
        return timer.store
      }
    },
    initialStore: function () {
      return {
        ms: duration,
        duration: duration
      }
    },
    view: function (timer) {
      // If running, then stop; if not running, then start
      function toggleRunning () {
        timer.emit(timer.states.running ? 'STOP' : 'START')
      }
      return h('div', [
        h('button.toggle', {
          props: {disabled: timer.states.finished},
          on: {click: toggleRunning}
        }, timer.states.running ? 'Pause' : 'Start'),
        h('button.reset', {
          on: {click: () => timer.emit('RESTART')},
          props: {disabled: timer.states.reset}
        }, 'Reset'),
        h('input', {
          props: {placeholder: 'Duration in ms', value: timer.store.duration, disabled: !timer.states.reset},
          on: {input: ev => timer.emit('SET_DUR', ev.currentTarget.value)}
        }),
        h('p', ['Currently ', JSON.stringify(timer.states)]),
        h('p', ['Time elapsed ', timer.store.ms, ' / ', timer.store.duration])
      ])
    }
  }
}

// We can render the component to the page by setting the .container property in the component

// First we instantiate the component
var config = Timer(1000)
// And set a container
var container = document.querySelector('#timer')
config.container = container
// Start rendering and generating vnodes
var timer = Component(config)
timer.vnode
timer.store
timer.emit

API

There is only one top-level function Component. It takes a config object and returns a component with .vnode, .store, .emit properties.

var Component = require('snabbdom-statechart-components')
var myComponent = Component(config)

The config object takes these keys:

  • trace: Show debug info in the console (default=false)
  • states: An array of all state names to include in your state chart. Required.
  • events: An object of event names mapped to pairs of state transitions in the form of [[fromState, toState]]. Required.
  • initialStates: An object of active initial states (eg. {opened: true, selected: false}). Required.
  • initialStore: A function that returns your initial store data on page load.
  • actions: An object where each key is an event name from events and each value is an updater function (see below)
  • view: A function that takes the component instance and an html function and returns a vnode. See below. Required.
  • nestedCharts: A set of nested statecharts. See below. Optional.

Action functions

Each updater function in the actions config property can update the store. An action function takes a component instance and any data as arguments and returns a new store.

The component instance has component.store and component.emit properties which you can use to read data and emit events.

The second argument to an action function is any arbitrary data that was emitted in a call to component.emit. For example, if you emit component.emit('ACTION', myData) then the action function for the event named 'ACTION' will have a second argument of myData.

View function

The view function takes an argument for the component instance.

You can read and print data out of the component by accessing component.store, and you can fire events from the dom with component.emit.

Import the h() function to generate html with require('snabbdom-statechart-componets/h'). Visit the snabbdom documentation for the details of its API. The snabbdom plugins eventlisteners, class, props, attributes, dataset and style are all enabled by this library.

Nested charts

You can have nested charts in your component under the .nestedCharts property. This property should have an object where every key is a valid state name, and ever value is a statechart object. The statechart

const config = {
  states: ['s1', 'nestedStateChart'],
  events: { PUSH: ['s1', 'nestedStateChart.initial'] },
  initialStates: {s1: true},
  actions: {...},
  nestedCharts: {
    nestedStateChart: {
      initial: {a1: true},
      states: ['a1']
    }
  },
  view: ...
})

The details of nesting state charts can be found in the harel documentation

Test utility

There is a simple test utility, which can be imported with require('snabbdom-statechart-components/test-util').

The test utility takes a component and an array of objects with event names, data, test functions, and delays (in milliseconds).

test('timer component', function (t) {
  var div = document.createElement('div')
  var timer = Component(Object.assign(Timer(1000), {container: div, trace: false}))
  testComponent(timer, [
    {
      event: 'SET_DUR',
      data: 999,
      test: function (timer) {
        t.strictEqual(timer.store.duration, 999)
        t.strictEqual(timer.store.ms, 999)
        t.deepEqual(timer.states, {reset: true})
      }
    },
    {
      event: 'START',
      test: function (timer) {
        t.deepEqual(timer.states, {running: true})
      }
    },
    {
      wait: 10,
      test: function (timer) {
        t.deepEqual(timer.states, {running: true})
        t.strictEqual(timer.store.ms, 989)
      }
    },
    {
      wait: 1100,
      test: function (timer) {
        t.deepEqual(timer.states, {finished: true})
        t.strictEqual(timer.store.ms, 0)
      }
    },
    {
      event: 'RESTART',
      test: function (timer) {
        t.deepEqual(timer.states, {reset: true})
        t.strictEqual(timer.store.ms, timer.store.duration)
        t.end()
      }
    },
    { event: 'START' },
    {
      wait: 10,
      event: 'STOP',
      function (timer) {
        t.deepEqual(timer.states, {paused: true})
      }
    }
  ])
})

Install

With npm installed, run

$ npm install harel-components

See Also

License

MIT