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

hover-engine

v2.1.0

Published

a state-management library that runs on technology and magic

Downloads

29

Readme

Hover-Engine

A state-management library that runs on predictable magic.

You've got to have POWER!

Hover-Engine is inspired by minidux, hover, and hyperapp so if you're familar with those frameworks, you'll notice some similarities!

Hover-Engine gives your app the following super-powers:

  • Trigger multiple sets of actions with a single dispatch!
  • Notify as many listeners as you want!
  • Handle async (or chained) action calls with predictable results!
  • No Dependencies!
  • Light enough to give you flight (~2 KB)!

Install

You can install hover-engine with npm like any other package.

npm install --save hover-engine

You can also include the umd distributable in your webpage directly with a script tag.

<head>
  <script src="https://unpkg.com/[email protected]/dist/hover-engine.umd.js" />
</head>

Usage

const HoverEngine = require('hover-engine')

const counterActions = {
  init: () => 0,
  increment: (state) => state + 1
}

const engine = new HoverEngine()
engine.addActions({ counter: counterActions })
engine.addListener((store) => console.log('NEW STATE:', store))

engine.actions.increment() // -> NEW STATE: { counter: 1 }
engine.actions.increment() // -> NEW STATE: { counter: 2 }
engine.store.counter // -> 2

API

constructor()

The constructor builds a new HoverEngine object. It takes in no parameters, and is immediately available for calling actions and store on (although they will be empty until you add actions).

Usage:

const engine = new HoverEngine()

addActions(actionGroups)

The addActions function adds new actions to the HoverEngine. It takes in a single object, whose keys will be used to get at the store, and whose values is an object mapping action names to functions.

Example actionGroup:

const actionGroup = {
  counter: {
    init: () => 0,
    increment: (counter) => counter + 1,
    decrement: (counter) => counter - 1
  }
}

In this example, counter is the key which you can use on the engine.store. The actions: init, increment, and decrement, are functions which will be called in engine.actions

const engine = new HoverEngine()
engine.addActions(actionGroup)  // adds the counter action group
engine.actions.increment()      // calls our increment function for counter
engine.store.counter            // returns the value at counter (in this case, 1)

The init action

const actionGroup = {
  counter: {
    init: () => 0
  }
}

All groups of actions must include the init action. This action dictates the initial state of the store value for those actions. In the above example, you'll notice that we set the initial value to 0. It is common to use zero, empty array, or empty string as inital values, but it is also a good place for default values.

The init action is called after engine.addActions runs. It is passed no arguments.

Action Arguments

const temperatureActions = {
  init: () => 70,

  increaseTemp: (temp) => temp + 1,

  setTemperature: (temp, newTemp) => newTemp,

  pullTemperatureFromZipcode: (temp, zipCode, actions) => {
    fetch('some.temperature.api/' + zipCode)
      .then((tempData) => actions.setTemperature(tempData))
  }
}

Excluding the init action, all actions are provided with the following arguments:

  • current state of the store (for this group of actions)
  • value passed into the action call
  • actions from the Hover-Engine

The first argument allows you to build a new state off of the existing one. In the example above, we increaseTemp from the value currently in the store.

const engine = new HoverEngine()
engine.addActions({temp: temperatureActions})
engine.actions.increaseTemp()
console.log(engine.store) // -> { temp: 71 }

The second argument is anything that we pass in when we call the action. For example:

const engine = new HoverEngine()
engine.addActions({temp: temperatureActions})
engine.actions.setTemperature(-10)
console.log(engine.store) // -> { temp: -10 }

In this example, we would set temperature -10 to the current value in the store.

The third argument is a reference to all available actions in Hover-Engine. These actions can be called and then are added to an existing queue of actions that get fired off one at a time to update the store.

const engine = new HoverEngine()
engine.addActions({temp: temperatureActions})
engine.actions.pullTemperatureFromZipcode('14623')
console.log(engine.store) // -> { temp: 76 }

This can be useful for async actions such as fetching, or when you need to call an action as a result of another action.

Adding Multiple Action Groups

With Hover-Engine, you can add multiple action groups two different ways. One way is by calling addActions multiple times. The other way, is by providing multiple sets of actions in the object you pass in, as shown below.

const todoActions = {
  todo: {
    init: () => [],
    addTodo: (todos, newTodo) => todos.concat(newTodo)
  },
  input: {
    init: () => '',
    setInput: (input, newInput) => newInput,
    addTodo: () => ''
  }
}
const engine = new HoverEngine()
engine.addActions(todoActions)    // adds the todo and input action groups
const newTodo = 'Buy Milk'
engine.actions.setInput(newTodo)  // store -> { todo: [], input: 'Buy Milk' }
engine.actions.addTodo(newTodo)   // store -> { todo: ['Buy Milk'], input: '' }

You'll notice in the above example that calling addTodo actually called both todo's addTodo and input's addTodo. We'll go over this more in the engine.actions section below.

addListener(listener)

Listeners in Hover-Engine are functions that get called whenever an action is called. It receives the updated engine.store, engine.actions, as well as information about the action that was called (read below).

const commentThreadActions = {
  init: () => [],
  addComment: (thread, newComment) => thread.concat(newComment)
}

const engine = new HoverEngine()
engine.addActions({thread: commentThreadActions})

engine.addListener((store) => document.body.innerHTML = store.thread.join('<br />'))

Like addActions, you can add as many listeners as you want by calling addListener multiple times. Each will be called with the new store.

Listener Arguments

Along with the store and actions, listeners also recieve the name of the action that was called, and the argument it was called with. With these, you can use listeners to debug what is happening in hover-engine. In the example below, we log the action and the new values in the store.

const debugListener = (store, actions, actionName, actionArguments) => {
  console.log(actionName, actionArguments, '->', store)
}

notifyListeners(actionName, actionArguments)

notifyListeners is a function which tells all the listeners to be triggered. It takes in an action name and action argument (both of which are optional), and calls all the listeners that have been added with the current store and actions, and passes along the action name and argument if they were included. You shouldn't need this in most applications, but can be useful for testing or debugging your logic.

const counterActions = {
  init: () => 0,
  increment: (counter) => counter + 1
}

const engine = new HoverEngine()
engine.addListener((store) => console.log('store:', store))
engine.notifyListeners() // store: {counter: 0}

Like addActions, you can add as many listeners as you want by calling addListener multiple times. Each will be called with the new store.

engine.actions

As shown above in the various examples above, engine.actions provides a means to call any actions that were added via addActions off of engine. In addition, actions is automatically a composition of all same-named actions.

const TimeZoneActions = {
  init: () => {status: 'NOT_LOADED', timezone: null},
  setTimezone: (state, newTimezone) => {status: 'LOADED', timezone: newTimezone},
  getTimezoneFromZipCode: (state, zipCode, actions) => {
    fetch('some.timezone.api/' + zipCode).then(
      (timezoneData) => {
        actions.setTimezone(timezone)
      }
    )
  },
  updateZipCode: (state, zipCode, actions) => {
    actions.getTimezoneFromZipCode(zipCode)
  }
}

const WeatherActions = {
  init: () => {status: 'NOT_LOADED', weather: null},
  setWeather: (state, newWeather) => {status: 'LOADED', weather: newWeather},
  getWeatherFromZipCode: (state, zipCode, actions) => {
    fetch('some.weather.api/' + zipCode).then(
      (weatherData) => actions.setWeather(weather)
    )
  },
  updateZipCode: (state, zipCode, actions) => {
    actions.getWeatherFromZipCode(zipCode)
  }
}

const engine = new HoverEngine()
engine.addActions({
  timezone: TimeZoneActions,
  weather: WeatherActions
})

engine.actions.updateZipCode('14623')

In the example above, same-named actions like updateZipCode will fire off for both the WeatherActions and the TimeZoneActions. It queues up weather.updateZipCode and timezone.updateZipCode. As both of those resolve, any actions that are triggered (like getWeatherFromZipCode and setWeather) will also get added to the queue in order, and each will take in an updated version of the state.

engine.store

The engine store exposes the current state of all the action groups. When you addActions the keys of the action object are the accessors for store values.