File size: 2,497 Bytes
bf237c2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import * as React from 'react';
import { create } from 'zustand';
import { subscribeWithSelector } from 'zustand/middleware';

// These are removed in Zustand v4
// unknown

// Zustand v3 marked deprecations in 3.x, but there's no visible upgrade path

const context = /* @__PURE__ */React.createContext(null);
function KeyboardControls({
  map,
  children,
  onChange,
  domElement
}) {
  const key = map.map(item => item.name + item.keys).join('-');
  const useControls = React.useMemo(() => {
    return create(subscribeWithSelector(() => map.reduce((prev, cur) => ({
      ...prev,
      [cur.name]: false
    }), {})));
  }, [key]);
  const api = React.useMemo(() => [useControls.subscribe, useControls.getState, useControls], [key]);
  const set = useControls.setState;
  React.useEffect(() => {
    const config = map.map(({
      name,
      keys,
      up
    }) => ({
      keys,
      up,
      fn: value => {
        // Set zustand state
        set({
          [name]: value
        });
        // Inform callback
        if (onChange) onChange(name, value, api[1]());
      }
    }));
    const keyMap = config.reduce((out, {
      keys,
      fn,
      up = true
    }) => {
      keys.forEach(key => out[key] = {
        fn,
        pressed: false,
        up
      });
      return out;
    }, {});
    const downHandler = ({
      key,
      code
    }) => {
      const obj = keyMap[key] || keyMap[code];
      if (!obj) return;
      const {
        fn,
        pressed,
        up
      } = obj;
      obj.pressed = true;
      if (up || !pressed) fn(true);
    };
    const upHandler = ({
      key,
      code
    }) => {
      const obj = keyMap[key] || keyMap[code];
      if (!obj) return;
      const {
        fn,
        up
      } = obj;
      obj.pressed = false;
      if (up) fn(false);
    };
    const source = domElement || window;
    source.addEventListener('keydown', downHandler, {
      passive: true
    });
    source.addEventListener('keyup', upHandler, {
      passive: true
    });
    return () => {
      source.removeEventListener('keydown', downHandler);
      source.removeEventListener('keyup', upHandler);
    };
  }, [domElement, key]);
  return /*#__PURE__*/React.createElement(context.Provider, {
    value: api,
    children: children
  });
}
function useKeyboardControls(sel) {
  const [sub, get, store] = React.useContext(context);
  if (sel) return store(sel);else return [sub, get];
}

export { KeyboardControls, useKeyboardControls };