Arthur: Shut up, will you, SHUT UP! Man: Aha! Now we see the violence inherent in the system! Arthur: SHUT UP! Man: Come and see the violence inherent in the system! HELP, HELP, I'M BEING REPRESSED! Arthur: Bloody PEASANT! Man: Oh, what a giveaway! Did'j'hear that, did'j'hear that, eh? That's what I'm all about! Did you see 'im repressing me? You saw it, didn't you?!
- Monty Python and the Holy Grail
Re-pressed is a library that handles keyboard events for re-frame applications.
[re-pressed "0.3.1"]
Note: if you are upgrading re-pressed from an earlier version, there was a breaking change - all instances of :which
should be replaced with :keyCode
. However, the upside is re-pressed no longer relies on jQuery!
And in your ns:
(ns your-ns
(:require [re-pressed.core :as rp]))
If you aren't careful, it is easy to add a bunch of keyboard event listeners scattered throughout your application. When these listeners collide, this can lead to unexpected and hard to debug behavior.
In addition, the current state of how to identify a keyboard event in a cross-browser compatible way can be quite cumbersome ... you will likely be asking yourself, "Should I use keyCode, key, which, etc?".
With re-pressed, you only set up one keyboard event listener when your
application starts, with ::rp/add-keyboard-event-listener
. However,
that does not mean that you are locked in to one set of rules for how
to handle keyboard events. By dispatching ::rp/set-keydown-rules
,
::rp/set-keypress-rules
, or ::rp/set-keyup-rules
, you can update
the rules dynamically.
In addition, Google Closure is able to ensure cross-browser compatibility with
their keyCode
attribute. Re-pressed trusts that Google Closure will do a good
job at keeping this current and uses it under the hood. The list of keycodes
can be found here.
::rp/add-keyboard-event-listener
::rp/add-keyboard-event-listener
adds the keyboard event listener to
your application. Needs to be dispatched only once, when the
application first loads.
There are three options, and you can use more than one if you'd like:
(re-frame/dispatch-sync [:prefix :application/keyboard
;; keydown
::rp/add-keyboard-event-listener "keydown"])
;; or
(re-frame/dispatch-sync [:prefix :application/keyboard
;; keypress
::rp/add-keyboard-event-listener "keypress"])
;; or
(re-frame/dispatch-sync [:prefix :application/keyboard
;; keyup
::rp/add-keyboard-event-listener "keyup"])
::rp/add-keyboard-event-listener
can have optional values to set
behaviors of event-listeners after the event-type.
:prefix
is mandatory on both ::rp/add-keyboard-event-listener
and rules setters (::rp/set-keydown-rules
::rp/set-keypress-rules
::rp/set-keyup-rules
) you should be consistent while passing prefix.
You can referance the each event's section.
;; While initializing Keyboard Event Listener:
(def prefix :com.flexiana/keyboard)
(re-frame/dispatch-sync [::rp/add-keyboard-event-listener "keydown"
:prefix prefix])
,,,
(re-frame/dispatch
[::rp/set-keydown-rules
;; it is important to be the same prefix
{:prefix prefix
:event-keys [,,,]
:clear-keys [,,,]
:always-listen-keys [,,,]
:prevent-default-keys [,,,]}])
;; or
(re-frame/dispatch-sync [::rp/add-keyboard-event-listener "keypress"
:prefix :com.flexiana/keyboard])
,,,
;; or
(re-frame/dispatch-sync [::rp/add-keyboard-event-listener "keyup"
:prefix :com.flexiana/keyboard])
,,,
keydown-keys
with Matched Event:clear-on-success-event-match
option is used to remove leftover
key-presses after a success match of events.
Default: false
.
(re-frame/dispatch-sync [::rp/add-keyboard-event-listener "keydown"
:prefix :bla
;; option-key: flag
:clear-on-success-event-match true])
;; or
(re-frame/dispatch-sync [::rp/add-keyboard-event-listener "keypress"
:prefix :bla
:clear-on-success-event-match true])
;; or
(re-frame/dispatch-sync [::rp/add-keyboard-event-listener "keyup"
:prefix :bla
:clear-on-success-event-match true])
::rp/set-keydown-rules
::rp/set-keydown-rules
takes a hash-map of :prefix
, :event-keys
,
:clear-keys
, :always-listen-keys
, and :prevent-default-keys
and
listens for keydown events.
:prefix
, this prefix is encapsulate your whole keyboard oriented
business-logic in one keyword under your application DB.:event-keys
, there is a vector of event + key combo vectors.
If any of the key combos are true, then the event will get
dispatched.:clear-keys
, there is a vector of key combo vectors. If any
of the key combos are true, then the recently recorded keys will be
cleared.:always-listen-keys
, there is a vector of just keys. If any
of the keys are pressed, then that key will always be recorded by
re-pressed. By default, keys are ignored when pressed inside of an input, select, or textarea.:prevent-default-keys
there is a vector of just keys. If any
of the keys are pressed, then the default browser action for that
key will be prevented.This is a description of the shape:
(re-frame/dispatch
[::rp/set-keydown-rules
{:event-keys [
[<event vector>
<key-combo vector>
...
<key-combo vectorN>]
]
:clear-keys [<key-combo vector>
...
<key-combo vectorN>]
:always-listen-keys [<key>
...
<keyN>]
:prevent-default-keys [<key>
...
<keyN>]
}])
Here is an example:
(re-frame/dispatch
[::rp/set-keydown-rules
{;; takes a collection of events followed by key combos that can trigger the event
:event-keys [
;; Event & key combos 1
[;; this event
[:some-event-id1]
;; will be triggered if
;; enter
[{:keyCode 13}]
;; or delete
[{:keyCode 46}]]
;; is pressed
;; Event & key combos 2
[;; this event
[:some-event-id2]
;; will be triggered if
;; tab is pressed twice in a row
[{:keyCode 9} {:keyCode 9}]
]]
;; takes a collection of key combos that, if pressed, will clear
;; the recorded keys
:clear-keys
;; will clear the previously recorded keys if
[;; escape
[{:keyCode 27}]
;; or Ctrl+g
[{:keyCode 71
:ctrlKey true}]]
;; is pressed
;; takes a collection of keys that will always be recorded
;; (regardless if the user is typing in an input, select, or textarea)
:always-listen-keys
;; will always record if
[;; enter
{:keyCode 13}]
;; is pressed
;; takes a collection of keys that will prevent the default browser
;; action when any of those keys are pressed
;; (note: this is only available to keydown)
:prevent-default-keys
;; will prevent the browser default action if
[;; Ctrl+g
{:keyCode 71
:ctrlKey true}]
;; is pressed
}])
For :event-keys
, :clear-keys
, :always-listen-keys
, and
:prevent-default-keys
, the keys take the following shape:
{:keyCode <int>
:altKey <boolean>
:ctrlKey <boolean>
:metaKey <boolean>
:shiftKey <boolean>
}
For :event-keys
, the event will be called with a few things conjed
on to the end of the event vector. For example:
;; this event
[:some-event-id1]
;; will be dispatched as
[:some-event-id1 js-event keyboard-keys]
Where:
js-event
is the javascript event of the most recently pressed keykeyboard-keys
is a collection of the recently pressed keys taking
the shape of the clojurescript hash-map described above.::rp/set-keypress-rules
Listens to keypress events, otherwise it is the same as ::rp/set-keydown-rules
(except :prevent-default-keys
is not supported).
::rp/set-keyup-rules
Listens to keyup events, otherwise it is the same as ::rp/set-keydown-rules
(except :prevent-default-keys
is not supported).
Create a new re-frame application and add the +re-pressed
option.
lein new re-frame foo +re-pressed
::rp/add-keyboard-event-listener
multiple times. This happens most often when using hot reload tools, such as figwheel, and you dispatch-sync the ::rp/add-keyboard-event-listener
event in a place that figwheel calls on reload ... instead of in a function that only gets called once when the application first starts.:prevent-default-keys
only works with
::rp/set-keydown-rules
. This is because the default browser action will
happen before keypress and keyup events happen.ctrl+n
in chrome.{:keyCode 37}
) and control + forward arrow ({:keyCode 37 :ctrlKey true}
), then you must put the combination before the singleton. Similarly, control-shift-arrow must come before control-arrow, and so on.When using re-pressed, you will need to dispatch a ::rp/set-keydown-rules
,
::rp/set-keypress-rules
, or ::rp/set-keyup-rules
event somewhere.
Personally, I like dispatching this in my routes file because I may
want to handle keyboard events differently on each page.
If you have questions, you can find social links on my profile @leavenha.
Copyright © 2018 Matthew Jaoudi
Copyright © 2019 Arne Schlüter
Copyright © 2022 Seçkin KÜKRER
Distributed under the The MIT License (MIT).
Can you improve this documentation? These fine people already did:
Matthew Jaoudi, Seçkin KÜKRER, James Marca & Søren KnudsenEdit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close