@svelte-put/shortcut GitHub

add keyboard shortcuts to node

@svelte-put/shortcut @svelte-put/shortcut @svelte-put/shortcut changelog

Installation

npm install --save-dev @svelte-put/shortcut
pnpm add -D @svelte-put/shortcut
yarn add -D @svelte-put/shortcut

Quick Start

The minimal example below shows how to register a global shortcut for Ctrl + K (Cmd + K on macOS) and log the attached node and the original trigger config.

<script lang="ts">
  import { shortcut, type ShortcutEventDetail } from '@svelte-put/shortcut';

  function handleK(detail: ShortcutEventDetail) {
    console.log('attached node:', detail.node);
    console.log('original trigger config:', detail.trigger);
  }
</script>

<svelte:window
  use:shortcut={{
    trigger: {
      key: 'k',
      modifier: ['ctrl', 'meta'],
      callback: handleK,
    },
  }}
/>

Trigger

The trigger option take either one single trigger definition…

<svelte:window
  use:shortcut={{ trigger: { key: 'k', modifier: 'ctrl' } }}
/>

…or multiple ones in an array…

<svelte:window
  use:shortcut={{
    trigger: [
      { key: 'c', modifier: 'ctrl' },
      { key: 'v', modifier: 'ctrl' },
    ],
  }}
/>

You can use multiple use:shortcut with one trigger definition in each.

<svelte:window
  use:shortcut={{ trigger: { key: 'c', modifier: 'ctrl' } }}
  use:shortcut={{ trigger: { key: 'v', modifier: 'ctrl' } }}
/>

This approach does take up some additional memory. It should be negligible in most cases but if your application is performance-critical, it is recommend to provide all triggers in one use:shortcut as shown in the multiple-triggers.svelte example.

Modifier

Each ShortcutTrigger can specify either one or multiple modifiers (ctrl, meta, alt, shift) via trigger.modifier in both AND & OR fashions.

Single Modifier

Set trigger.modifier to one of the modifier strings (ctrl, meta, alt, shift) to listen for that modifier.

<svelte:window
  use:shortcut={{
    // ctrl+k
    trigger: {
      key: 'k',
      modifier: 'ctrl',
    },
  }}
/>

One of Many Modifiers (OR)

Use a flat array of modifier strings to trigger when one of them is pressed.

<svelte:window
  use:shortcut={{
    // ctrl+k or meta+k
    trigger: {
      key: 'k',
      modifier: ['ctrl', 'meta'],
    },
  }}
/>

All of Modifiers (AND)

Wrap multiple modifier strings in a subarray to trigger only when all modifiers are pressed at the same time.

<svelte:window
  use:shortcut={{
    // ctrl+shift+K
    trigger: {
      key: 'K',
      modifier: [['ctrl', 'shift']],
    },
  }}
/>

Notice that the key option in the above example is set to a capital K. This is because shift is specified as a modifier; when shift and k are pressed down at the same time, KeyboardEvent.key value will be K. You can use this site to test your key combinations.

Mix & Match

Use a combination of OR and AND to create complex modifier combinations.

<svelte:window
  use:shortcut={{
    // ctrl+alt+Delete or meta+Delete
    trigger: {
      key: 'Delete',
      modifier: [['ctrl', 'alt'], 'meta'],
    },
  }}
/>

Handler

Handlers can be provided via either trigger.callback

<script lang="ts">
  import { shortcut, type ShortcutEventDetail } from '@svelte-put/shortcut';

  function toggleCommandPalette(detail: ShortcutEventDetail) {
    console.log('Action was placed on:', details.node);
    console.log('Trigger:', details.trigger);
    // ...
  }
</script>
<svelte:window
  use:shortcut={{
    trigger: {
      key: 'k',
      modifier: ['ctrl', 'meta'],
      callback: toggleCommandPalette,
    },
  }}
/>

…or on:shortcut CustomEvent:

<script lang="ts">
  import { shortcut, type ShortcutEventDetail } from '@svelte-put/shortcut';

  function handleShortcuts(event: CustomEvent<ShortcutEventDetail>) {
    if (event.detail.trigger.id === 'toggle-command-palette') {
      // ...
    }
  }
</script>

<svelte:window
  use:shortcut={{
    trigger: {
      key: 'k',
      modifier: ['ctrl', 'meta'],
      id: 'toggle-command-palette',
    },
  }}
  on:shortcut={handleShortcuts}
/>

trigger.id is specified definition in the handler-custom-event.svelte example to conveniently help identify the trigger in the on:shortcut event listener.

The two approaches are equivalent and depend on your aesthetic preference when multiple shortcuts are defined. trigger.callback is bound directly to its trigger definition, whereas on:shortcut is a centralized event listener for all shortcuts.

You should use only one of the two presented approaches and NOT both to avoid duplicate event handling.

Original KeyboardEvent

You can access the original KeyboardEvent via detail.originalEvent. This is helpful for checking target.

<script lang="ts">
  import type { ShortcutEventDetail } from '@svelte-put/shortcut';
  import { shortcut } from '@svelte-put/shortcut';

  function onShortcut(event: CustomEvent<ShortcutEventDetail>) {
    const keyboardEvent = event.detail.originalEvent;
    // be cautious: `keyboardEvent` has already reached window here

    keyboardEvent.preventDefault(); // prevent browser default

    if ((keyboardEvent.target as HTMLElement)?.tagName === 'INPUT') {
      console.log('some input is focused, should skip');
      return;
    }
    // do things
  }
</script>

<svelte:window
  use:shortcut={{
    trigger: {
      key: 'k',
      modifier: ['ctrl', 'meta'],
    },
  }}
  on:shortcut={onShortcut}
/>

Be aware that the event listener is placed on the same node the shortcut action is attached to. For example, if you use the action on svelte:window, and trigger a matching shortcut from an <input> element, calling stopPropagation or preventDefault on originalEvent might not result in the behavior you would expected. By the time trigger.callback or on:shortcut event handler runs, the event has already bubbled up to window.

API References

It is recommended to utilize your language server and intellisense for API exploration. In advanced use cases, however, you can refer to shortcut’s type definitions.


Happy making shortcuts! 👨‍💻

Edit this page on Github