Giter Club home page Giter Club logo

vue-global-events's Introduction

vue-global-events Build Status npm package

Add shortcuts by listening to events on the document, anywhere

This is the version for Vue 3, if you are looking for the Vue 2 version, take a look at the v1 branch

Sponsors

Bronze

Vue Mastery logo Vue Jobs logo

Installation

yarn add vue-global-events
npm install vue-global-events

Idea

Thanks to Vue’s event modifiers, handling events is extremely easy however, you’re limited to DOM element events. We decided to change that, so now you can register global events (for example application shortcuts) just like you would listen to events on a component. No need to worry about removing them either. You can toggle the events with a single v-if. Works with SSR too.

Usage

import { GlobalEvents } from 'vue-global-events'

// register globally
app.component('GlobalEvents', GlobalEvents)

// or locally
export default {
  components: { GlobalEvents },
  // rest of your component
}

After that you can register global events like this:

<GlobalEvents
  v-if="listenersConnected"
  @keyup.ctrl.tab="nextTab"
  @keyup.ctrl.shift.tab="previousTab"
  @keyup.space="pause"
  @contextmenu="openMenu"
/>

Props

target

Target element where addEventListener is called on. It's a String that refers to a global variable like document or window. This allows you to add events to the window instead of document.

  • type: String
  • default: 'document'

Warning: This prop is not reactive. It should be provided as a static value. If you need it to be reactive, add a key attribute with the same value:

<GlobalEvents :target="target" :key="target" />

filter

Function to prevent any event from being executed based on anything related to the event like the element that triggered it, the name, or the handler.

  • type: Function
  • default: () => true
arguments
  • event: Native Event Object
  • handler: method passed to GlobalEvents component
  • eventName: event name that was attached to the target (See above)

filter should return false to prevent the execution of a handler. For example, you can avoid the calls if the event is triggered by an <input>:

<GlobalEvents
  :filter="(event, handler, eventName) => event.target.tagName !== 'INPUT'"
  @keyup.prevent.space.exact="nextTab"
/>

In the example above event would be the native keyup Event Object, handler would be the method nextTab and eventName would be the string keyup.

Advice / Caveats

  • Always .prevent events with .ctrl and other modifiers as browsers may be using them as shortcuts.
  • Do not use shortcuts that are used by the system or that the browser does not allow you to .preventDefault(). The list includes Ctrl+Tab/Cmd+Tab, Ctrl+W/Cmd+W. You can find more information in this StackOverflow answer.
  • Prefer using actual characters to keyCodes whenever possible: @keydown.+ for detecting the plus sign. This is important because symbols and numbers on the digit row will provide different keyCodes depending on the layout used.
  • About using keyup with modifiers like .ctrl or .shift: the keyup event is triggered when a key is released and that's also when the event.ctrlKey is checked, which if you just released, will be false. This is because ctrl, shift and alt are checked differently. If you want to trigger on the keyup event of a modifier, you need to use its keycode (check it here. For example, for the ctrl key, that would be: @keyup.17. You can also use the advice above this one to provide it a name like ctrlkey.

Development

Run tests in watch mode:

yarn jest --watch

Authors

Damian Dulisz @shentao

Eduardo San Martin Morote @posva

License

MIT

This project was created using the Vue Library boilerplate by posva

vue-global-events's People

Contributors

dependabot[bot] avatar posva avatar therenard 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

vue-global-events's Issues

[Feature Request]: add "parent" keyword to `target`

Related to #13 it would be useful to be able to target the immediate parent of a GlobalEvents instance, so the parent could manage keypresses within it.

My use case would be to provide different keyboard shortcuts per component (say, an editor component) for different operating systems:

<RichTextEditor>
  <GlobalEvents v-if="isMac" target="parent" @keyup.meta.b="formatBold" />
  <GlobalEvents v-if="isWin" target="parent" @keyup.ctrl.b="formatBold" />
  ...
<RichTextEditor>

Suggestion: Allow different event targets

It would be cool if I can specify different event target other than document like window and body. Probably using modifiers or arguments with the bindings: @scroll.window, @scroll:body.

Filter feature not in npm release

Hi @posva,
Really useful addition to Vue!
I noticed that you haven't yet released 1.0.3 with Filter support.
Any chance you can release that? I'd like to use npm to stay up-to-date with future releases.

Add double tap key helper

I am building a web app and have all my keyboard shortcuts set with this package.
Thank you so much. ❤️

One keyboard shortcut I would like to set is "double tap the a key" (or any other key)

I know it's currently not possible, but I'd love to have this feature in the near future!!! 🐬

Listen to two events at the same time?

I need help from javascript expert. I would like to listen to two events (mousewheel and keydown) at the same time. I can get control buttons with wheel event (ctrl, alt, shift), but not other keys (I tried with event.key, event.which, event.code). Is it possible at all? This is my code:

methods: {
    wheelCommands(e) {
      if (e.deltaY < 0 && e.ctrlKey) {
        e.preventDefault()
        console.log('Scrolling up. OK.')
      }
      if (e.deltaY > 0 && e.ctrlKey) {
        e.preventDefault()
        console.log('Scrolling down. OK.')
      }
      if (e.deltaY > 0 && e.ctrlKey && e.key === 'a') {
        e.preventDefault()
        console.log('Scrolling down? NO WAY. ')
      }
      if (e.deltaY > 0 && e.key === 'a') {
        e.preventDefault()
        console.log('Scrolling down? NO WAY. ')
      }
    },
  },
  created() {
    window.addEventListener('keydown', this.wheelCommands, { passive: false })
    window.addEventListener('wheel', this.wheelCommands, { passive: false })
  },
  destroyed() {
    window.removeEventListener('keydown', this.wheelCommands, { passive: false })
    window.removeEventListener('wheel', this.wheelCommands, { passive: false })
  }

Sorry for using issues as help forum. Any hint highly appreciated.

Doesn't work with vue 3

Even the simplest empty invocation of VueGlobalEvents (<GlobalEvents />) in a vue 3 app will render a blank page with an uncaught error in the console log.

Remove DOM element

Love the package, used it for ages, thanks a million for making it!

Is it possible to include the component without it's DOM element? Currently I remove the DOM element with CSS but it would be nice if this wasn't there in the first place as I can't see what value or features it gives being included.

render: h => h(),

The render function simply calls createElement, but could it be replaced with h => null,? Which I believe would make Vue render the component out as a comment: <!-- -->

I can't see anywhere in the codebase that actually uses $el so I don't believe this would have a knock on effect to the package itself, but I could be missing something that might make it a breaking change for user of the package. Would be good to get other people thoughts.

Keyup for shift and ctrl modifiers does not work in the latest 3.0.0 version

Reproduction

https://jsfiddle.net/25yx04oq/

Steps to reproduce the bug

We have used this code with Vue 2 and worked fine. Now with Vue 3 @keyup.17 and @keyup.16 does not trigger any more.

`<script src="https://unpkg.com/vue@3"></script>

<script src="https://unpkg.com/vue-global-events@2"></script>

Ctrl and Shift demo

Control: {{ isControl }}
Shift: {{ isShift }}

const { createApp } = Vue

createApp({
el: '#app',
components: {
GlobalEvents: VueGlobalEvents.GlobalEvents
},
data: () => ({
isControl: false,
isShift: false,
})
}).mount('#app')`

Expected behavior

@keyup.16 and @keyup.17 should be trigerred

Actual behavior

@keyup.16 and @keyup.17 are not trigerred

Additional information

@keydown.control and @keyup.control is triggered correctly. Is there a similar option for shift key?

error in ./node_modules/@vue/reactivity/dist/reactivity.esm-bundler.js

Hi,

I'm using your module with Vue3 and I keep hitting this error. I have a fresh Vue CLI project with minimal config, but keep hitting it. I see this on "npm run serve". The error is as follows:

vue-cli-service serve

INFO Starting development server...
98% after emitting CopyPlugin

ERROR Failed to compile with 1 error > 14:48:24

error in ./node_modules/@vue/reactivity/dist/reactivity.esm-bundler.js

Module parse failed: Unexpected token (763:13)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See >https://webpack.js.org/concepts#loaders
| }
| class RefImpl {

_rawValue;

| _shallow;
| _value;

@ ./node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.js 1:0-233 2:0-216 2:0-216 2:0-216 2:0-216 2:0-216 >2:0-216 2:0-216 2:0-216 2:0-216 2:0-216 2:0-216 2:0-216 2:0-216 2:0-216 2:0-216 2:0-216 2:0-216 2:0-216 2:0-216 16:4->17 40:4-17 107:13-18 108:32-37 115:16-21 1958:8-13 1962:13-23 1968:35-45 1970:16-21 1973:21-31 2071:19-25 2094:8-12 >2135:8-13 2210:29-34 2557:27-30 2558:26-29 2559:28-31 2905:16-29 2912:16-29 3043:28-36 3362:41-56 3378:28-33 >3463:8-15 3500:32-37 3647:27-32 3823:29-34 3951:12-25 3958:12-25 4568:17-22 4592:13-18 5173:26-32 5336:8-21 >5340:8-21 5755:16-20 5761:12-16 6296:27-32 6328:12-19 6338:16-23 6356:93-100 6357:15-20 6767:60-75 6768:60-75 >6769:60-75 6770:59-74 6847:16-21 6997:16-21 7150:21-28 7160:8-21 7161:134-149 7162:8-21 7210:30-39 7264:8-21 >7266:8-21 7314:23-38 7334:46-55 7334:56-63 7389:14-24 7576:21-26 7586:21-31 7593:24-34 7596:21-31 7624:53-58 >7630:52-57 7696:48-53
@ ./node_modules/@vue/runtime-dom/dist/runtime-dom.esm-bundler.js
@ ./node_modules/vue/dist/vue.runtime.esm-bundler.js
@ ./src/main.js
@ multi (webpack)-dev-server/client?http://192.168.86.249:8080&sockPath=/sockjs-node (webpack)/hot/dev-server.js >./src/main.js

My code is as follows in App.vue

    <template>  
      <div>
        <GlobalEvents @keyup.space="pause" />
      </div>
    </template>

    <script>
    import { GlobalEvents } from "vue-global-events";
    
    export default {
      name: "App",
      components: {
        GlobalEvents,
      },
      methods: {
        pause() {
          console.log(`Pause`);
        },
      },
    };
    </script>

More comprehensive docs and examples

I'm happy to write these docs but right now it isn't super clear how to set keylisteners for stuff like "period".

Ideally there would be exhaustive docs for every key code.

This module is great! Thank you!!!

Is it possible to test it with vue-test-utils and trigger() helper?

I'm using, GlobalEvents inside a component. While testing that component with vue-test-utils and jest I'm trying to verify keyboard events related features. But I cannot use trigger on it:

  TypeError: Cannot read property 'length' of undefined

      54 |    wrapper.vm.show()
      55 |    expect(wrapper.find(GlobalEvents).exists).toBeTruthy()
    > 56 |    wrapper.find(GlobalEvents).trigger('keydown.down')
         |                               ^

      at VueWrapper.attributes (node_modules/@vue/test-utils/dist/vue-test-utils.js:3247:34)
      at VueWrapper.trigger (node_modules/@vue/test-utils/dist/vue-test-utils.js:3754:12)
      at Object.trigger (spec/javascript/ventas_pos/modals/modal_caja_spec.js:56:31)

Of course I'm not using shallowMount.

There is some way to accomplish this? Thanks

Version in dist items is not up to date

Additional information

This maybe nitpicking and not a functional problem but a documentation issue. It initially got me wondering whether my project's build process was broken:

The header of all items in the dist folder of the npm package of vue-global-event is still saying "v2.1.0" when in fact, the release is "v2.1.1". See directly in the npm package or via their online source browser: https://www.npmjs.com/package/vue-global-events?activeTab=explore

I suggest updating and releasing a fix with version 2.1.2.

is Vue 2 still supported ?

I've tested your example downgrading to vue 2.7.10 and it doesn't work (but no errors in console sadly) So I wonder if it's still supported.

If only vue 3 is supported please update the doc to make it explicit

Unexpected event behaviour with [email protected]

Version

vue-global-events: 1.1.1
vue: anything above 2.6.0-beta.1

Steps to reproduce

Use an inline expression to set a boolean value inside the key data of the component. The value of it starts as being false.

<a href="#" @click="key = !key">Example</a>

Have a global event listener that is only active if key is true.

<global-events
    v-if="key"
    @click="key = false"
/>

What is expected?

Clicking the link will set key to true if it starts by being false. The handler for the global events should not be triggered. This is behaviour in [email protected] and below.

What is actually happening?

key is set to true by the handler of the link click event. The global events handler is also triggered and key is set to false, right after the initial event of the a tag.

Add typescript declaration

Can't use this with vue-cli

29:26 Could not find a declaration file for module 'vue-global-events'. '/Users/risto/git/foo/node_modules/vue-global-events/dist/vue-global-events.cjs.js' implicitly has an 'any' type.
  Try `npm install @types/vue-global-events` if it exists or add a new declaration (.d.ts) file containing `declare module 'vue-global-events';`

Add a warning or try to use withMacroTask

So event handler here are different from Vue's event handlers in one important aspect: they use microtasks instead of macrotasks. So if an event handler here modifies Vue reactive state, Vue responds to that change immediately, even before the event handler returns, which can have bad effects (like invalidating DOM so that the event cannot bubble at all and things like that; my experience was that events would randomly seem like not triggering).

Vue wraps it handlers with withMacroTask so this component should do the same. The only issue is that withMacroTask is not seems to be available outside of Vue codebase.

So maybe there should be a warning to use setTimeout(..., 0) inside your handler if you are modifying any reactive state.

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.