All files / event / keyboard.ts

100.00% Branches 35/35
100.00% Functions 1/1
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
x12
x12
x36
x1
x1
x35
x40
x59
x59
x59
x59
x59
x26
x7
x7
x19
x59
x3
x2
x2
x1
x59
x10
x6
x6
x4
x59
x20
x11
x11
x59
x59
x14
x35
x12
x12


































































/**
 * 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): (event: KeyboardEvent) => boolean {
  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
    })
  }
}