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

keypound

v1.2.0

Published

A library for handling keyboard shortcuts through multiple levels of an app.

Downloads

21

Readme

Keypound.js

Scoped key binding library with context stacking and ES6 out of the box. Based on Keymaster.js

Build Status

This is a dependency-free library aimed at making scoped key bindings in large apps a breeze.

Sometimes in large applications, you need the same shortcut to perform different tasks in different places. For instance, you might need the up and down arrows to move around different lists depending on where your focus was last.

Often we resort to using event.stopPropagation() and event.preventDefault(), to prevent the keys from bubbling up, but we should really be creating a context stack and telling our code which context we are in, and which context will next receive events that haven't been handled.

Getting Started

Installation

  • Using npm:
npm install keypound
  • Using yarn:
yarn add keypound

Creating an instance

To get started with keypound, you'll need to create a master instance:

// master.js
import Keypound from 'keypound'

const master = new Keypound(options)
export default master

Registering a context

After creating a master instance you can import it into any module and enter your first context

// my-module.js

import master from './master'

const context = master.enter('module-context')

Binding to a shortcut

Once you've registered a context, you can start binding some shortcuts!

context.on('down, tab', () => this.moveDown())
context.on('up, shift + tab', () => this.moveUp())

This will cause moveDown to fire anytime the down arrow or tab key is pressed. Likewise, moveUp will fire anytime the up arrow or shift-tab is pressed.

Multiple contexts

Anytime the user enters a new context (open a modal or dialog) you tell the master instance to enter a new context and add your new bindings.

const modalContext = master.enter('modal-context')

Every new context stacks on top of the contexts that have already been entered. All bindings are given to the context at the top of the stack (the most recent) and then fall down the stack until they are handled.

Once a shortcut is handled, it will stop cascading down the stack.

Re-entering contexts

If a user goes back to a previous context without exiting the current one (maybe they clicked on a different area of the app), you can re-enter that context with all the same bindings, but now it is at the top of the stack.

You do this by calling enter() with the same context name.

master.enter('previous-context')

Afterwards, all bindings will take top priority once again.

Exiting contexts

When a modal closes, you will want to remove that context from the stack entirely. There are two ways to do this.

  1. Exit the context from the master:
  master.exit('modal-context')
  1. Exit the context using the context reference:
  modalContext.exit()

Pausing and playing contexts

Sometimes you will need to keep a contexts spot in the stack but stop your shortcut handlers from firing.

Thankfully, you can call .pause() on your context to pause any handling, and then call .play() when you are ready to start responding to shortcuts again.

Note: Since your handlers will not be firing, any duplicate shortcuts down the stack will still fire. To prevent this, either have your handler decide whether or not to do anything, or use the block: true option when creating your context for the first time.

API

Below are the interfaces for each object type in the library

Keypound instance

You should really only have one of these in your app. If you need more, make sure you are careful about which is responsible for what.

Constructor

const master = new Keypound(options);

Options

When creating a new Keypound, you can provide the following options

{
  keyEvent: 'keydown', // the event type to listen for
  createRoot: true, // create a root context for the app
  defaultFilter: (event, binding) => boolean, // a global function to filter events, can be overridden by each context
}

Methods

  • enter(contextName, options)
    • returns Context instance used for shortcut bindings.
  • exit(contextName)
    • returns Context instance if found, null if there was no match.
  • getStackIndex(contextName)
    • returns number of where the context lies in the stack.

Context options

When creating a new context, you can supply options for how shortcuts are handled:

  • block: boolean: if true, stops all shortcuts at this context, even if they have no handler.
  • filter: (event, binding) => boolean: Local filter for events in this context, defaults to the defualtFilter set in the master.

Context instance

Methods

  • on(shortcut, handler, options)
    • shortcut: See Creating a shortcut
    • handler: See handler
    • options: any of the following
      • prevent: boolean: call event.preventDefault if set to true
  • off(shortcut): removes a shortcuts handler from the context.
  • exit(): removes the context from the master stack.
  • pause(): temporarily stop handlers from firing and allow shortcuts to cascade the stack.
  • play(): resume handling of shortcuts.

Creating a shortcut

Shortcuts are written in as [modifiers] + [key] combos, separated by commas. Everything below is a valid shortcut:

  • d, control + m
  • alt + C
  • shift + p
  • ⇧ + ⌘ + left
  • space, ctrl + space, alt + tab
  • ctrl + alt + delete

Supported keys

Keypound understands the following modifiers: , shift, option, , alt, ctrl, control, command, and .

The following special keys can be used for shortcuts: backspace, tab, clear, enter, return, esc, escape, space, up, down, left, right, home, end, pageup, pagedown, del, delete and f1 through f19.

Handler

Each handler is given two arguments, event and binding.

  • event: The native keydown event that matched the shortcut
  • binding: The information associated with the binding
    • key: a number representing the keyCode that triggered the event
    • mods: a map of KeyEvent modifiers and a boolean indicating if they are required for the shortcut.
    • options: any options passed to the initial binding
    • handler: the callback itself

Contributing

To contribute, please fork Keypound.js, add your patch and tests for it (in the test/specs folder), run npm test, and submit a pull request.

TODO

  • More docs
  • More examples
  • Better edge-case testing
  • Filtering
  • Aliases