{"id":16411388,"url":"https://github.com/gadfly361/re-pressed","last_synced_at":"2026-04-02T02:08:34.074Z","repository":{"id":31972490,"uuid":"129660147","full_name":"gadfly361/re-pressed","owner":"gadfly361","description":"re-pressed is a clojurescript library that handles keyboard events for re-frame applications.","archived":false,"fork":false,"pushed_at":"2023-08-01T19:13:50.000Z","size":8267,"stargazers_count":164,"open_issues_count":0,"forks_count":14,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-09-29T16:15:58.591Z","etag":null,"topics":["cljs","clojurescript","keyboard","keyboard-events","keydown","keypress","keyup","re-frame","reagent"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gadfly361.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2018-04-15T23:33:33.000Z","updated_at":"2025-05-26T19:00:37.000Z","dependencies_parsed_at":"2022-08-08T16:15:15.835Z","dependency_job_id":"d08e28bb-bb17-4376-bcdd-b1beda70fb04","html_url":"https://github.com/gadfly361/re-pressed","commit_stats":{"total_commits":36,"total_committers":8,"mean_commits":4.5,"dds":0.5,"last_synced_commit":"b36e06023764f13baadc99e0dfebe4616c5f6a5c"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/gadfly361/re-pressed","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gadfly361%2Fre-pressed","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gadfly361%2Fre-pressed/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gadfly361%2Fre-pressed/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gadfly361%2Fre-pressed/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gadfly361","download_url":"https://codeload.github.com/gadfly361/re-pressed/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gadfly361%2Fre-pressed/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31294399,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T01:43:37.129Z","status":"online","status_checked_at":"2026-04-02T02:00:08.535Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cljs","clojurescript","keyboard","keyboard-events","keydown","keypress","keyup","re-frame","reagent"],"created_at":"2024-10-11T06:45:19.138Z","updated_at":"2026-04-02T02:08:34.053Z","avatar_url":"https://github.com/gadfly361.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# re-pressed\n\n\u003e **Arthur**: Shut up, will you, SHUT UP!\n\u003e **Man**:\tAha!  Now we see the violence inherent in the system!\n\u003e **Arthur**: SHUT UP!\n\u003e **Man**:\tCome and see the violence inherent in the system!\tHELP, HELP, I'M BEING REPRESSED!\n\u003e **Arthur**: Bloody PEASANT!\n\u003e **Man**:\tOh, 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?!\n\u003e - Monty Python and the Holy Grail\n\nRe-pressed is a library that handles keyboard events\nfor [re-frame](https://github.com/Day8/re-frame) applications.\n\n```clojure\n[re-pressed \"0.3.2\"]\n```\n\nNote: there is also a [fork of re-pressed](https://github.com/M-sleeper/re-pressed) that fixes this [issue](https://github.com/gadfly361/re-pressed/issues/13) (i.e. the `re-frame: Subscribe was called outside of a reactive context` warnings). There are trade-offs between this repo and the fork. This repo has the console warnings but guarantees event ordering, while the fork fixes the console warnings, but doesn't guarantee event ordering. To use the fork, you can use:\n\n```clojure\n[org.clojars.betontalpfa/re-pressed \"0.4.0\"]\n```\n\n![re-pressed gif not found](re-pressed.gif)\n\n# The Problem\n\nIf you aren't careful, it is easy to add a bunch of keyboard event\nlisteners scattered throughout your application. When these listeners\ncollide, this can lead to unexpected and hard to debug behavior.\n\nIn addition, the current state of how to identify a keyboard event in\na cross-browser compatible way can be quite cumbersome ... you will\nlikely be asking yourself, \"Should I use keyCode, key, which, etc?\".\n\n# Re-pressed's Solution\n\nWith re-pressed, you only set up one keyboard event listener when your\napplication starts, with `::rp/add-keyboard-event-listener`. However,\nthat does not mean that you are locked in to one set of rules for how\nto handle keyboard events. By dispatching `::rp/set-keydown-rules`,\n`::rp/set-keypress-rules`, or `::rp/set-keyup-rules`, you can update\nthe rules dynamically.\n\nIn addition, Google Closure is able to ensure cross-browser compatibility with\ntheir `keyCode` attribute. Re-pressed trusts that Google Closure will do a good\njob at keeping this current and uses it under the hood.  The list of keycodes \ncan be found [here](https://github.com/google/closure-library/blob/master/closure/goog/events/keycodes.js#L27).\n\n# API\n\n### `::rp/add-keyboard-event-listener`\n\n`::rp/add-keyboard-event-listener` adds the keyboard event listener to\nyour application. Needs to be dispatched **only once**, when the\napplication *first* loads.\n\nThere are three options, and you can use more than one if you'd like:\n\n```clojure\n(re-frame/dispatch-sync [::rp/add-keyboard-event-listener \"keydown\"])\n\n;; or\n(re-frame/dispatch-sync [::rp/add-keyboard-event-listener \"keypress\"])\n\n;; or\n(re-frame/dispatch-sync [::rp/add-keyboard-event-listener \"keyup\"])\n```\n\n### Event Listeners Options\n\n `::rp/add-keyboard-event-listener` can have optional values to set\n behaviors of event-listeners after the event-type.\n\n Such as;\n\n #### Clear `keydown-keys` with Matched Event\n\n `:clear-on-success-event-match` option is used to remove leftover\n key-presses after a success match of events.\n\n Default: `false`.\n\n ``` clojure\n (re-frame/dispatch-sync [::rp/add-keyboard-event-listener \"keydown\" :clear-on-success-event-match true])\n\n ;; or\n (re-frame/dispatch-sync [::rp/add-keyboard-event-listener \"keypress\" :clear-on-success-event-match true])\n\n ;; or\n (re-frame/dispatch-sync [::rp/add-keyboard-event-listener \"keyup\" :clear-on-success-event-match true])\n\n ```\n\n\n### `::rp/set-keydown-rules`\n\n`::rp/set-keydown-rules` takes a hash-map of `:event-keys`,\n`:clear-keys`, `:always-listen-keys`, and `:prevent-default-keys` and\nlistens for *keydown* events.\n\n- For `:event-keys`, there is a vector of *event + key combo* vectors.\n  If any of the key combos are true, then the event will get\n  dispatched.\n- For `:clear-keys`, there is a vector of *key combo* vectors. If any\n  of the key combos are true, then the recently recorded keys will be\n  cleared.\n- For `:always-listen-keys`, there is a vector of just *keys*. If any\n  of the keys are pressed, then that key will always be recorded by\n  re-pressed. *By default, keys are ignored when pressed inside of an input, select, or textarea.*\n- For `:prevent-default-keys` there is a vector of just *keys*. If any\n  of the keys are pressed, then the default browser action for that\n  key will be prevented.\n\nThis is a description of the shape:\n\n```clojure\n(re-frame/dispatch\n [::rp/set-keydown-rules\n  {:event-keys [\n                [\u003cevent vector\u003e\n                 \u003ckey-combo vector\u003e\n                 ...\n                 \u003ckey-combo vectorN\u003e]\n                ]\n\n   :clear-keys [\u003ckey-combo vector\u003e\n                ...\n                \u003ckey-combo vectorN\u003e]\n\n   :always-listen-keys [\u003ckey\u003e\n                        ...\n                        \u003ckeyN\u003e]\n\n   :prevent-default-keys [\u003ckey\u003e\n                          ...\n                          \u003ckeyN\u003e]\n\n   }])\n```\n\nHere is an example:\n\n```clojure\n(re-frame/dispatch\n [::rp/set-keydown-rules\n  {;; takes a collection of events followed by key combos that can trigger the event\n   :event-keys [\n                ;; Event \u0026 key combos 1\n                [;; this event\n                 [:some-event-id1]\n                 ;; will be triggered if\n                 ;; enter\n                 [{:keyCode 13}]\n                 ;; or delete\n                 [{:keyCode 46}]]\n                ;; is pressed\n\n                ;; Event \u0026 key combos 2\n                [;; this event\n                 [:some-event-id2]\n                 ;; will be triggered if\n                 ;; tab is pressed twice in a row\n                 [{:keyCode 9} {:keyCode 9}]\n                 ]]\n\n   ;; takes a collection of key combos that, if pressed, will clear\n   ;; the recorded keys\n   :clear-keys\n   ;; will clear the previously recorded keys if\n   [;; escape\n    [{:keyCode 27}]\n    ;; or Ctrl+g\n    [{:keyCode   71\n      :ctrlKey true}]]\n   ;; is pressed\n\n   ;; takes a collection of keys that will always be recorded\n   ;; (regardless if the user is typing in an input, select, or textarea)\n   :always-listen-keys\n   ;; will always record if\n   [;; enter\n    {:keyCode 13}]\n   ;; is pressed\n\n   ;; takes a collection of keys that will prevent the default browser\n   ;; action when any of those keys are pressed\n   ;; (note: this is only available to keydown)\n   :prevent-default-keys\n   ;; will prevent the browser default action if\n   [;; Ctrl+g\n    {:keyCode   71\n      :ctrlKey true}]\n    ;; is pressed\n   }])\n```\n\n\nFor `:event-keys`, `:clear-keys`, `:always-listen-keys`, and\n`:prevent-default-keys`, the keys take the following shape:\n\n```clojure\n{:keyCode    \u003cint\u003e\n :altKey   \u003cboolean\u003e\n :ctrlKey  \u003cboolean\u003e\n :metaKey  \u003cboolean\u003e\n :shiftKey \u003cboolean\u003e\n }\n```\n\nFor `:event-keys`, the *event* will be called with a few things *conj*ed\non to the end of the event vector. For example:\n\n```\n;; this event\n[:some-event-id1]\n\n;; will be dispatched as\n[:some-event-id1 js-event keyboard-keys]\n```\n\nWhere:\n\n- `js-event` is the javascript event of the most recently pressed key\n- `keyboard-keys` is a collection of the recently pressed keys taking\n  the shape of the clojurescript hash-map described above.\n\n\n### `::rp/set-keypress-rules`\n\nListens to *keypress* events, otherwise it is the same as `::rp/set-keydown-rules` (except `:prevent-default-keys` is not supported).\n\n### `::rp/set-keyup-rules`\n\nListens to *keyup* events, otherwise it is the same as `::rp/set-keydown-rules` (except `:prevent-default-keys` is not supported).\n\n# Usage\n\nCreate a new re-frame application and add the `+re-pressed` option.\n\n```\nlein new re-frame foo +re-pressed\n```\n\n## Gotchas\n\n- If you are seeing multiple instances of the same event being triggered, it is probably because you are dispatching `::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.\n- For keypress events, you only have access to things like letters and\n  numbers. This is unlike keydown and keyup events, where you have\n  access to more things like the Escape key.\n- Using `:prevent-default-keys` only works with\n  `::rp/set-keydown-rules`. This is because the default browser action will\n  happen before keypress and keyup events happen.\n- Certain browser default actions cannot be overwritten, like `ctrl+n`\n  in chrome.\n- Order matters, and the first matching key combination will consume the event.  So for example, if you want to listen for both forward arrow (`{: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.\n  \n### Side note:\n\nWhen using re-pressed, you will need to dispatch a `::rp/set-keydown-rules`,\n`::rp/set-keypress-rules`, or `::rp/set-keyup-rules` event somewhere.\nPersonally, I like dispatching this in my routes file because I may\nwant to handle keyboard events differently on each page.\n\n## Questions\n\nIf you have questions, I can usually be found hanging out in\nthe [clojurians](http://clojurians.net/) #reagent slack channel (my\nhandle is [@gadfly361](https://twitter.com/gadfly361)).\n\n## License\n\nCopyright © 2018 Matthew Jaoudi\n\nCopyright © 2019 Arne Schlüter\n\nDistributed under the The MIT License (MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgadfly361%2Fre-pressed","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgadfly361%2Fre-pressed","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgadfly361%2Fre-pressed/lists"}