All files / event / keyboard.ts

100.00% Branches 19/19
100.00% Lines 37/37
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
x7
x19
x19
x55
x56
x56
x90
x130
x189
x189
x189
x189
x189
x215
x222
x222
x234
x189
x192
x194
x194
x193
x189
x199
x214
x214
x212
x189
x209
x220
x220
x189
x189
x144
x90
x19
x19


































































/**
 * Keyboard expression matcher.
 *
 * Multiple keys can be combined into a single combination using a plus sign (`+`).
 * Multiple combinations can be combined into a single expression using a comma (`,`).
 * Spaces are always trimmed.
 *
 * The following aliases are supported:
 * - `alt` when `Alt` is pressed
 * - `ctrl` when `Control` is pressed
 * - `shift` when `Shift` is pressed
 * - `meta` when `Meta` is pressed
 * - `space` for `Space` key
 * - `key` for any key except `Alt`, `Control`, `Shift` and `Meta`
 *
 * If the event is not a {@link https://developer.mozilla.org/docs/Web/API/KeyboardEvent | KeyboardEvent}, the function will return `false`.
 *
 * {@link https://developer.mozilla.org/docs/Web/API/UI_Events/Keyboard_event_key_values | Reference}
 *
 * ```ts
 * import { Window } from "@mizu/internal/vdom"
 * const { KeyboardEvent } = new Window()
 *
 * const check = keyboard("a,ctrl+b")
 * console.assert(check(new KeyboardEvent("keydown", {key: "a"})))
 * console.assert(check(new KeyboardEvent("keydown", {key: "b", ctrlKey: true})))
 * console.assert(!check(new KeyboardEvent("keydown", {key: "c"})))
 * ```
 *
 * @author Simon Lecoq (lowlighter)
 * @license MIT
 */
export function keyboard(keys: string) {
  const combinations = keys.split(",").map((combination) => combination.split("+").map((key) => key.trim().toLowerCase()))
  return function (event: KeyboardEvent) {
    if (!/^key(?:down|press|up)$/.test(event.type)) {
      return false
    }
    return combinations.some((combination) => {
      for (const key of combination) {
        switch (key) {
          case "alt":
          case "ctrl":
          case "shift":
          case "meta":
            if (!event[`${key}Key`]) {
              return false
            }
            break
          case "space":
            if (event.key !== " ") {
              return false
            }
            break
          case "key":
            if (/^(?:alt|ctrl|shift|meta)$/i.test(event.key)) {
              return false
            }
            break
          default:
            if (event.key.toLowerCase() !== key) {
              return false
            }
        }
      }
      return true
    })
  }
}