Giter VIP home page Giter VIP logo

angular-hotkeys's Introduction

angular-hotkeys

Configuration-centric keyboard shortcuts for your Angular apps.

Coverage Status Build Status

Requirements: Angular 1.2+

Features:

  • Define hotkeys on an entire route, automatically binding and unbinding them as you navigate
  • Automatic listing of shortcuts when users hit the ? key
  • Super duper unit tests

Installation:

via bower:

$ bower install chieffancypants/angular-hotkeys --save

via npm:

$ npm install angular-hotkeys --save

please use either the minified or unminified file in the build directory

Why I made this:

Other projects out there rely too heavily on HTML markup for keyboard shortcuts. For example:

<div class="player">
  <div class="playPause-btn" hotkey="{space: playPause}"></div>
  <div class="mute-btn" hotkey="{'ctrl+down': mute}"></div>
</div>

While this is a great approach for many Angular apps, some applications do not have a 1 to 1 relationship between DOM elements and controller methods. In my case, many methods on the controller were only accessible through the keyboard.

Additionally, this only allows you to pass a function reference, you can't pass arguments to the function you intend to call. So instead of simply calling seek(currentTime + 30) and seek(currentTime + 60), I needed to create a ton of helper functions on the scope (such as forward30 and forward60), and litter my HTML like this:

<div class="player" hotkey="{space: playPause,
                              'alt+right': forward30,
                              'ctrl+right': forward60,
                              'left': back30,
                              'ctrl+left': back60,
                              up: volumeUp,
                              down: volumeDown,
                              'ctrl+down': mute,
                              'ctrl+up': unmute,
                              f: fullscreen,
                              h: showHelp}">
  <div class="playPause-btn"></div>
  <div class="mute-btn"></div>
</div>

With a few dozen shortcuts, this left the DOM really messy, and with multiple views and directive templates, it was next to impossible to remember where all the different shortcuts were. This became a maintenance nightmare.

Usage:

You can either define hotkeys in your Controller, or in your Route configuration (or both). To start, though, require the lib as a dependency for your angular app:

angular.module('myApp', ['ngRoute', 'cfp.hotkeys']);

Behind the scenes, I'm using the Mousetrap library to manage the key bindings. Check out the docs there for more information on what kind of key combinations can be used. This library is included in the files from the build directory, so there is no need to install and include Mousetrap separately.

Update: A YouTube video tutorial was created for this project. Thanks guys!

Binding hotkeys in controllers:

It is important to note that by default, hotkeys bound using the hotkeys.add() method are persistent, meaning they will continue to exist through route changes, DOM manipulation, or anything else.

However, it is possible to bind the hotkey to a particular scope, and when that scope is destroyed, the hotkey is automatically removed. This should be considered the best practice when binding hotkeys from a controller. For this usage example, see the hotkeys.bindTo() method below:

angular.module('myApp').controller('NavbarCtrl', function($scope, hotkeys) {
  $scope.volume = 5;

  // You can pass it an object.  This hotkey will not be unbound unless manually removed
  // using the hotkeys.del() method
  hotkeys.add({
    combo: 'ctrl+up',
    description: 'This one goes to 11',
    callback: function() {
      $scope.volume += 1;
    }
  });

  // when you bind it to the controller's scope, it will automatically unbind
  // the hotkey when the scope is destroyed (due to ng-if or something that changes the DOM)
  hotkeys.bindTo($scope)
    .add({
      combo: 'w',
      description: 'blah blah',
      callback: function() {}
    })
    // you can chain these methods for ease of use:
    .add ({...});

});

Binding hotkeys in routes:

You can also define hotkeys on an entire route, and this lib will bind and unbind them as you navigate the app.

angular.module('myApp').config(function ($routeProvider) {
  $routeProvider.when('/', {
    controller: 'RestaurantsController',
    templateUrl: 'views/restaurants.html',
    hotkeys: [
      ['p', 'Sort by price', 'sort(price)']
    ]
  });
});

Binding hotkeys in directives:

Lastly, even though binding hotkeys in your templates/html tends to be a bad idea, it can be super useful for simple shortcuts. Think along the lines of a modal directive where you simply want to bind to the escape key or something equally simple. Accomplishing this within a controller is too much overhead, and it may lead to code-reuse.

Example of how directive-based hotkeys works:

<modal title="Modal Title" hotkey="{esc: close}">

Cheatsheet

A cheatsheet is created automatically for you, showing which hotkeys are available for the current route, along with a description as to what it does. The default binding to show the cheatsheet is ?. Be sure to include the build/hotkeys.css stylesheet. Cheatsheet demo

Disable the cheatsheet:

Disabling the cheatsheet can be accomplished by configuring the hotkeysProvider:

angular.module('myApp', ['cfp.hotkeys'])
  .config(function(hotkeysProvider) {
    hotkeysProvider.includeCheatSheet = false;
  })

Configuration

Disable ngRoute integration:

To prevent listening for $routeChangeSuccess events use hotkeysProvider. This option defaults to false if ngRoute module is not loaded:

angular.module('myApp', ['cfp.hotkeys'])
  .config(function(hotkeysProvider) {
    hotkeysProvider.useNgRoute = false;
  })

Cheatsheet template:

angular.module('myApp', ['cfp.hotkeys'])
  .config(function(hotkeysProvider) {
    hotkeysProvider.template = '<div class="my-own-cheatsheet">...</div>';
  })

Header and footer:

You can specify a custom header and footer for the cheatsheet. Both are HTML, and if the header is set it will override the normal title.

angular.module('myApp', ['cfp.hotkeys'])
  .config(function(hotkeysProvider) {
    hotkeysProvider.templateHeader = '<div class="my-own-header">...</div>';
    hotkeysProvider.templateFooter = '<div class="my-own-footer">...</div>';
  })

API

hotkeys.add(object)

object: An object with the following parameters:

  • combo: They keyboard combo (shortcut) you want to bind to
  • description: [OPTIONAL] The description for what the combo does and is only used for the Cheat Sheet. If it is not supplied, it will not show up, and in effect, allows you to have unlisted hotkeys.
  • callback: The function to execute when the key(s) are pressed. Passes along two arguments, event and hotkey
  • action: [OPTIONAL] The type of event to listen for, such as keypress, keydown or keyup. Usage of this parameter is discouraged as the underlying library will pick the most suitable option automatically. This should only be necessary in advanced situations.
  • allowIn: [OPTIONAL] an array of tag names to allow this combo in ('INPUT', 'SELECT', and/or 'TEXTAREA')
hotkeys.add({
  combo: 'ctrl+w',
  description: 'Description goes here',
  callback: function(event, hotkey) {
    event.preventDefault();
  }
});

// this hotkey will not show up on the cheat sheet:
hotkeys.add({
  combo: 'ctrl+x',
  callback: function(event, hotkey) {...}
});

hotkeys.get(key)

Returns the Hotkey object

hotkeys.get('ctrl+w');
// -> Hotkey { combo: ['ctrl+w'], description: 'Description goes here', callback: function (event, hotkey) }

hotkeys.del(key)

Removes and unbinds a hotkey

hotkeys.del('ctrl+w');

Allowing hotkeys in form elements

By default, Mousetrap prevents hotkey callbacks from firing when their event originates from an input, select, or textarea element. To enable hotkeys in these elements, specify them in the allowIn parameter:

hotkeys.add({
  combo: 'ctrl+w',
  description: 'Description goes here',
  allowIn: ['INPUT', 'SELECT', 'TEXTAREA'],
  callback: function(event, hotkey) {
    event.preventDefault();
  }
});

Credits:

Muchas gracias to Craig Campbell for his Mousetrap library, which provides the underlying library for handling keyboard shortcuts.

angular-hotkeys's People

Contributors

3komma14 avatar aloker avatar anaphase avatar atticoos avatar cbess avatar chieffancypants avatar darabos avatar douglasduteil avatar dubitabam avatar hypercubed avatar joshbowdoin avatar margru avatar maximilianoricotabo avatar mprokopowicz avatar nickwoodhams avatar noullet avatar ofpiyush avatar paulftw avatar pauljoey avatar pkaminski avatar richtmat avatar shaddyz avatar sisidovski avatar willhoag avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

angular-hotkeys's Issues

angular-hotkeys blocks some taps / clicks in certain places.

I am working on an app, which generates and displays a long list of elements with an ng-repeat from an API. Each of the elements is clickable in various places.

I noticed after installing hotkeys that I couldn't click on some of the elements as there was an invisible <div class="cfp-hotkeys-container fade ng-scope" ng-class="{in: helpVisible}"><div class="cfp-hotkeys"> ... </div></div> overlying them.

directive : allowIn

<mydirective hotkey="{up: upKey,allowIn: ['INPUT', 'SELECT', 'TEXTAREA']}"  > ... 

how can i set 'allowIn' option inside a directive. The one above doesn't work.

Hotkey to give focus to a field results as last hotkey beeing typed

Hi!

First of all, congrats for your work on this lib! It is really awesome.

I ran into a problem when setting an hotkey to give the focus to a field :

hotkeys.add({
            hotkey: 't a',
            description: 'Feel field',
            callback: function() {
                jQuery('#field').focus().val('');
            }
        });

When a press t and then a the field is correctly focused, but the a is typed inside the field, which I don't want.

Do you have any solution to solve this problem?

Thank you for your help

Regards

[angular 1.3.0-beta.5] Cheatsheet not appended to DOM

On the latest version of Angular, appending to $rootElement doesn't appear to do anything. As a test, if I change the following line to append directly to the body, everything works as expected:

// existing, broken
angular.element($rootElement).append($compile(helpMenu)(scope));

// works
angular.element(document.body).append($compile(helpMenu)(scope));

I don't see any existing issues on the angular repo or anything related to $rootElement in the changelog, so I'm not sure what the expected behavior is.

Hide hotkeys popup while CSS is loading

There is currently a built-in assumption that the CSS will be fully-loaded and parsed before angular-hotkeys executes and injects cfp-hotkeys-container. If the CSS is not loaded, then the unstyled hotkeys helper will be visible. This happens in our case where we load hotkeys dynamically and asynchronously. The fix is relatively simple:

Add style="visibility: hidden;" to cfp-hotkeys-container and .cfp-hotkeys-container.fade.in { visibility: visible !important; } to the CSS

TypeError: Cannot read property 'replace' of undefined

As soon as I start trying to add hotkeys to my application I start getting this error on page navigation:

TypeError: Cannot read property 'replace' of undefined
at _bindSingle (/scripts/vendor-app.js:3033:34)
at _bindMultiple (/scripts/vendor-app.js:3080:13)
at Object.Mousetrap.bind (/scripts/vendor-app.js:3107:13)
at Object.Mousetrap.unbind (/scripts/vendor-app.js:3129:30)
at Object._del as del
at HTMLAnchorElement. (/scripts/vendor-app.js:2225:19)
at HTMLAnchorElement.b.event.dispatch (http://192.168.1.106:5001/scripts/vendor-app.js:3:28337)
at HTMLAnchorElement.b.event.add.v.handle (/scripts/vendor-app.js:3:25049)
at Object.b.event.trigger (/scripts/vendor-app.js:3:27423)
at b.fn.extend.triggerHandler (/scripts/vendor-app.js:4:3188)

Looks like on the view $destroy event, _del is being called with undefined here:

  function _del (hotkey) {
    var combo = (hotkey instanceof Hotkey) ? hotkey.combo : hotkey;
    Mousetrap.unbind(combo);

Which eventually ends up calling this:

  function _bindSingle(combination, callback, action, sequenceName, level) {

    // store a direct mapped reference for use with Mousetrap.trigger
    _directMap[combination + ':' + action] = callback;

    // make sure multiple spaces in a row become a single space
    combination = combination.replace(/\s+/g, ' ');

Which throws an exception since combination is undefined. I tried to start over by commenting out all of my hotkeys. but the problem persisted until I remove the module from my app.

Bugs

hotkeys seems to get confused between similarly named keypresses

For exampe.. check this plnkr
http://plnkr.co/edit/SV24o4ZRB6UEXfGlt8F2

When i hit t+c it should go to option1-module but instead it goes to option2-module which has a shortcut of c+c

CFP hotkey for input also types hotkey in input

.add({
      combo: 's',
      description: 'Search for new tweets',
      callback: function() {
        $('.input input').trigger('focus');
        $scope.searchTweets = '';
      }
    })

$('.input input') then has the letter 's' in it and is focused.

Hotkeys in textboxes, textareas, etc

Hi again. Is hotkeys supposed to work while focused on textareas or inputs? I get hotkeys working on my app when none of those are in focus, but I'd really like to specify hotkeys on textareas. If it's just a config issue, I'd love to know what the trick is. Thanks. :)

Persistence across routes

Hotkeys are persisted when declared in controllers, according to your comments because controllers are persisted, but controllers are re-instantiated by ngRoute... it makes sense to persist hotkeys declared in services, but does it really in controllers? Minimally, could you expose an option on the provider to configure whether hotkeys are persisted by default?

Two letter combos do not seem to work

Two letter combinations, e.g. g+t or g+g don't seem to work unless I add in a dummy hotkey of g +.

hotkeys.add({
  combo: 'g +',
  description: 'Select tab',
  callback: function () {
    // dummy hotkey function to get two letter combos working
  }
});
hotkeys.add({
  combo: 'g+t',
  description: 'Select Table tab',
  callback: function () {
    _.where($scope.tabs, {
      heading: 'Table'
    })[0].active = true;
  }
});

Esc can re-open cheasheet

  1. open cheatsheet
  2. click close button with mouse
  3. hit Esc

Expect behavior: no action
Actual behavior: cheatsheet is re-opened

Esc binding conflicts with "Help" Esc binding

I'm seeing my Esc bindings disappear after opening up help. When help opens it replaces the esc binding, and deletes on esc. Would you be open to swapping the key command to preserve esc bindings on the page? Internally, instead of the _add/_del cycle, we could _tempAdd/_tempDel or preserve previous bindings through some other mechanism.

Right now, I'm just remapping Esc to Ctrl+Esc in toggleCheatSheet.

Enhancement request: make help menu disableable

Currently, the help menu is always appended to the document and there seems to be no way to prevent this. Would be nice to have this configurable, i.e. make an option that allows us to disable the help menu.

del() method doesn't work with multiple keys

Hi, I have added multiple keys using the following syntax:
hotkeys.add({ combo: ['1','2','3','4','5','6','7','8','9'],....

but the del() method seems don't work.
hotkeys.del(['1','2','3','4','5','6','7','8','9']);

Am I doing something wrong?

One more thing: when I use multiple keys the cheatSheet doesn't display all keys, as already notified in this issue #15 .

Thanks in advance

Restrict cheat sheet to controllers

Is there a way to restrict the displaying of the cheat sheet to only opted in controllers?

My angular app is defined as:

var myApp = angular.module('myApp', ['cfp.hotkeys']);

Because of this, where ever I have an angular controller the cheat sheet can be show.

Here is a controller where I want the cheat sheet to show:

myApp.controller('myController1', ['$scope', '$http', 'hotkeys', 'databoostrapped', function ($scope, $http, hotkeys, databoostrapped) {

Here I do not want it to show:

myApp.controller('dashboardController', ['$scope', '$http', 'databoostrapped', function ($scope, $http, databoostrapped) {

I am assuming as I do not have hotkeys defined for this controller it should not appear?

I am new to angularjs so I am just trying to understand the logic.

If I create a new App I am sure that the cheat sheet will not appear...

Allow duplicate hotkeys

If I try to bind two actions independently to the same hotkey, the second replaces the first. However there are some totally valid use cases for wanting this. For example, binding to Esc in multiple modal windows or error messages and wanting Esc to close all of them.

If this is actually a Mousetrap limitation, let me know and I'll file it over there instead.

multiplied bindings

Hi, we'reusing angular-hotkyes in our project.

On site index we have welcome page, on another site home there are some actions with added hotkeys.

When I navigate from index to home and show help through ? everything is fine. Then I navigate to index and back to home and keys are doubled (then tripled, ... etc.)

Duplicate Hotkeys when having Callback Function and defined on Route

Hi,

Thanks for your work, I really like what you did...
I report you some problem / bug I found when using it on my project (https://github.com/davinkevin/Podcast-Server, check it to see the exact config)

I use the bleeding-edge version of Angular (right now, v1.3.0-build.2805+sha.751ebc1) and all the dependancy in the same version.

I think there is a bug in the line 361 :

scope.hotkeys.push(new Hotkey(combo, description, callback, action, allowIn, persistent));

because you call the constructor 114

function Hotkey (combo, description, callback, action, persistent) {

And forget to add the allowIn option or align correctly the persistent parameters.
The result is the persistent parameter to be transformed in an empty array preventing its deletion.

I don't do a PR because I don't know if there is some side effect to this bug, so I let you do the modification.

Thanks

Integration with Angular UI-Router

hi,

this is great project, but as most of us use Angular UI Router, is there a way to integrate with it instead of Angular Default Router?

thanks in advanced.

Hotkey scoping

If I set hotkeys in a controller, it should ideally be scoped to the controller. If the scope for that controller is destroyed, the hotkeys should similarly be destroyed.

pull #23 regression

looks related to #23

TypeError: 'undefined' is not an object (evaluating 'target.nodeName.toUpperCase')
        at /home/wes/.../bower_components/angular-hotkeys/build/hotkeys.js:336

CFP div causing errors with clicking

The .cfp-hotkeys-container should be display: none; to prevent issues with z-index and not being able to click on elements underneath the container.

Hotkeys in cheatsheet

If I am using directives, the cheatsheet does not seem to pick them up.

Can this be done using directives?

For example the element looks like:

Hotkeys fails with Tinymce/iFrame

If the user's cursor is in an iFrame, as it is with tinymce or any other text editor, hotkeys no longer works. There should be some way to configure hotkeys and add document objects for it to listen to.

Displaying cheatsheet unbinds the esc key

I binded the 'esc' key with

hotkeys.add('esc', 'Close search', function(event){
        event.preventDefault();
        $scope.closeSearch();
    });

And it works great but after displaying the cheatsheet with '?' the 'esc' binding does not work anymore. The next time I open the cheatsheet the 'esc' binding does not appear.

Thank you for sharing chieffancypants ๐Ÿ‘

Hotkeys cleared on page reload?

If you define hotkeys in a property on the route configuration, they're bound on $routeChangeSuccess. However, if you reload the same route, $routeChangeSuccess is not fired again and all of the hotkeys are cleared out during bootstrap. Is there a way to persist hotkeys defined in the route configuration through reloading the same page?

Allow customizing the cheatsheet title

Hey,
The app I'm developing is in portuguese, so I need to change the cheatsheet title.
It's probably a good call to add a cheatSheetTitle property (as there already are other cheatSheet* properties).

Typo in documentation

There is a typo in the documentation.
this:

<modal title="Modal Title" hotkeys="{esc: close}">

Should be:

<modal title="Modal Title" hotkey="{esc: close}"> 

:)

hotkeys in ng-repeat

<foo ng-repeat="m in members" hotkey="{esc: test}">

</foo>

As code shows, it seems only the last directive will receive the esc event

missing parameter "allowIn" in constructor

in version 1.3.0
missing parameter "allowIn" in Hotkey constructor and its assignment

line 108 (definition): function Hotkey (combo, description, callback, action, persistent) {
line 355 (usage): scope.hotkeys.push(new Hotkey(combo, description, callback, action, allowIn, persistent));

impacts:

  1. allowIn does not work properly
  2. it gets even mixed up with persistent and it does not work properly either

Problem with bower.json

It looks like there are two angular-hotkeys packages in bower angular-hotkeys and chieffancypants/angular-hotkeys. chieffancypants/angular-hotkeys is the package I want because it is up-to-date.

But in my bower.json this line:

"dependencies": {
  "chieffancypants/angular-hotkeys": "~1.1.0",
}

causes the error bower ENOTFOUND Package chieffancypants/angular-hotkeys=chieffancypants/angular-hotkeys not found

If I change my dependency to this:

"dependencies": {
  "angular-hotkeys": "~1.1.0",
}

It will fail because the bower only has [email protected].

I'm new bower and angular so this might be entirely my problem, but my guess is that the un-namespaced angular-hotkeys needs to be unpublished from bower?

Cheatsheet showing behind content

Hey there, great project. :) What assumption do you make about the application html/css? The z-index:-1024 is clearly designed to push the cheatsheet out of view before the user hits ?, but on my app, it shows as a background behind my app functionality

image

I put body { background-color: white; z-index: 0; } but didn't seem to make any difference.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.