@rokkit/actions 1.0.0-next.38 → 1.0.0-next.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/lib/internal.js +54 -28
- package/src/pannable.js +21 -18
- package/src/swipeable.js +65 -70
- package/src/traversable.js +55 -42
- package/src/types.js +58 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rokkit/actions",
|
|
3
|
-
"version": "1.0.0-next.
|
|
3
|
+
"version": "1.0.0-next.39",
|
|
4
4
|
"description": "Contains generic actions that can be used in various components.",
|
|
5
5
|
"author": "Jerry Thomas <me@jerrythomas.name>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"validators": "latest",
|
|
24
24
|
"vite": "^4.4.7",
|
|
25
25
|
"vitest": "~0.33.0",
|
|
26
|
-
"shared-config": "1.0.0-next.
|
|
26
|
+
"shared-config": "1.0.0-next.39"
|
|
27
27
|
},
|
|
28
28
|
"files": [
|
|
29
29
|
"src/**/*.js",
|
package/src/lib/internal.js
CHANGED
|
@@ -1,39 +1,23 @@
|
|
|
1
1
|
import { compact } from '@rokkit/core'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* @
|
|
7
|
-
* @
|
|
8
|
-
* @
|
|
9
|
-
* @
|
|
10
|
-
* @property {Function} [expand]
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* @typedef {Object} NavigationOptions
|
|
15
|
-
* @property {Boolean} [horizontal]
|
|
16
|
-
* @property {Boolean} [nested]
|
|
17
|
-
* @property {Boolean} [enabled]
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* @typedef {Object} KeyboardActions
|
|
22
|
-
* @property {Function} [ArrowDown]
|
|
23
|
-
* @property {Function} [ArrowUp]
|
|
24
|
-
* @property {Function} [ArrowRight]
|
|
25
|
-
* @property {Function} [ArrowLeft]
|
|
26
|
-
* @property {Function} [Enter]
|
|
27
|
-
* @property {Function} [Escape]
|
|
28
|
-
* @property {Function} [" "]
|
|
4
|
+
* Emits a custom event with the given data.
|
|
5
|
+
*
|
|
6
|
+
* @param {HTMLElement} element
|
|
7
|
+
* @param {string} event
|
|
8
|
+
* @param {*} data
|
|
9
|
+
* @returns {void}
|
|
29
10
|
*/
|
|
11
|
+
export function emit(element, event, data) {
|
|
12
|
+
element.dispatchEvent(new CustomEvent(event, { detail: data }))
|
|
13
|
+
}
|
|
30
14
|
|
|
31
15
|
/**
|
|
32
16
|
* Maps keyboard events to actions based on the given handlers and options.
|
|
33
17
|
*
|
|
34
|
-
* @param {ActionHandlers} handlers
|
|
35
|
-
* @param {NavigationOptions} options
|
|
36
|
-
* @returns {KeyboardActions}
|
|
18
|
+
* @param {import('../types').ActionHandlers} handlers
|
|
19
|
+
* @param {import('../types').NavigationOptions} options
|
|
20
|
+
* @returns {import('../types').KeyboardActions}
|
|
37
21
|
*/
|
|
38
22
|
export function mapKeyboardEventsToActions(handlers, options) {
|
|
39
23
|
const { next, previous, select, escape } = handlers
|
|
@@ -56,8 +40,50 @@ export function mapKeyboardEventsToActions(handlers, options) {
|
|
|
56
40
|
})
|
|
57
41
|
}
|
|
58
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Finds the closest ancestor of the given element that has the given attribute.
|
|
45
|
+
*
|
|
46
|
+
* @param {HTMLElement} element
|
|
47
|
+
* @param {string} attribute
|
|
48
|
+
* @returns {HTMLElement|null}
|
|
49
|
+
*/
|
|
59
50
|
export function getClosestAncestorWithAttribute(element, attribute) {
|
|
60
51
|
if (!element) return null
|
|
61
52
|
if (element.getAttribute(attribute)) return element
|
|
62
53
|
return getClosestAncestorWithAttribute(element.parentElement, attribute)
|
|
63
54
|
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Sets up event handlers based on the given options.
|
|
58
|
+
* Returns whether or not the event handlers are listening.
|
|
59
|
+
*
|
|
60
|
+
* @param {HTMLElement} element
|
|
61
|
+
* @param {import('../types').EventHandlers} listeners
|
|
62
|
+
* @param {import('../types').TraversableOptions} options
|
|
63
|
+
* @returns {void}
|
|
64
|
+
*/
|
|
65
|
+
export function setupListeners(element, listeners, options) {
|
|
66
|
+
const { enabled } = { enabled: true, ...options }
|
|
67
|
+
|
|
68
|
+
if (enabled) {
|
|
69
|
+
Object.entries(listeners).forEach(([event, listener]) =>
|
|
70
|
+
element.addEventListener(event, listener)
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Removes event handlers based on the given options.
|
|
77
|
+
* Returns whether or not the event handlers are listening.
|
|
78
|
+
*
|
|
79
|
+
* @param {HTMLElement} element
|
|
80
|
+
* @param {import('../types').EventHandlers} listeners
|
|
81
|
+
* @returns {void}
|
|
82
|
+
*/
|
|
83
|
+
export function removeListeners(element, listeners) {
|
|
84
|
+
if (listeners) {
|
|
85
|
+
Object.entries(listeners).forEach(([event, listener]) => {
|
|
86
|
+
element.removeEventListener(event, listener)
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
}
|
package/src/pannable.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { removeListeners, setupListeners } from './lib'
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Handle drag and move events
|
|
3
5
|
*
|
|
@@ -7,6 +9,18 @@
|
|
|
7
9
|
export function pannable(node) {
|
|
8
10
|
let x
|
|
9
11
|
let y
|
|
12
|
+
let listeners = {
|
|
13
|
+
primary: {
|
|
14
|
+
mousedown: start,
|
|
15
|
+
touchstart: start
|
|
16
|
+
},
|
|
17
|
+
secondary: {
|
|
18
|
+
mousemove: move,
|
|
19
|
+
mouseup: stop,
|
|
20
|
+
touchmove: move,
|
|
21
|
+
touchend: stop
|
|
22
|
+
}
|
|
23
|
+
}
|
|
10
24
|
|
|
11
25
|
function track(event, name, delta = {}) {
|
|
12
26
|
x = event.clientX || event.touches[0].clientX
|
|
@@ -20,37 +34,26 @@ export function pannable(node) {
|
|
|
20
34
|
)
|
|
21
35
|
}
|
|
22
36
|
|
|
23
|
-
function
|
|
37
|
+
function start(event) {
|
|
24
38
|
track(event, 'panstart')
|
|
25
|
-
window
|
|
26
|
-
window.addEventListener('mouseup', handleMouseup)
|
|
27
|
-
window.addEventListener('touchmove', handleMousemove, { passive: false })
|
|
28
|
-
window.addEventListener('touchend', handleMouseup)
|
|
39
|
+
setupListeners(window, listeners.secondary)
|
|
29
40
|
}
|
|
30
41
|
|
|
31
|
-
function
|
|
42
|
+
function move(event) {
|
|
32
43
|
const dx = (event.clientX || event.touches[0].clientX) - x
|
|
33
44
|
const dy = (event.clientY || event.touches[0].clientY) - y
|
|
34
45
|
|
|
35
46
|
track(event, 'panmove', { dx, dy })
|
|
36
47
|
}
|
|
37
48
|
|
|
38
|
-
function
|
|
49
|
+
function stop(event) {
|
|
39
50
|
track(event, 'panend')
|
|
40
|
-
|
|
41
|
-
window.removeEventListener('mousemove', handleMousemove)
|
|
42
|
-
window.removeEventListener('mouseup', handleMouseup)
|
|
43
|
-
window.removeEventListener('touchmove', handleMousemove)
|
|
44
|
-
window.removeEventListener('touchend', handleMouseup)
|
|
51
|
+
removeListeners(window, listeners.secondary)
|
|
45
52
|
}
|
|
46
53
|
|
|
47
|
-
node
|
|
48
|
-
node.addEventListener('touchstart', handleMousedown, { passive: false })
|
|
54
|
+
setupListeners(node, listeners.primary)
|
|
49
55
|
|
|
50
56
|
return {
|
|
51
|
-
destroy()
|
|
52
|
-
node.removeEventListener('mousedown', handleMousedown)
|
|
53
|
-
node.removeEventListener('touchstart', handleMousedown)
|
|
54
|
-
}
|
|
57
|
+
destroy: () => removeListeners(node, listeners.primary)
|
|
55
58
|
}
|
|
56
59
|
}
|
package/src/swipeable.js
CHANGED
|
@@ -6,87 +6,82 @@
|
|
|
6
6
|
* @returns {import('./types').SvelteActionReturn}
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
let
|
|
20
|
-
let startX
|
|
21
|
-
let startY
|
|
22
|
-
let startTime
|
|
9
|
+
import { removeListeners, setupListeners } from './lib'
|
|
10
|
+
const defaultOptions = {
|
|
11
|
+
horizontal: true,
|
|
12
|
+
vertical: false,
|
|
13
|
+
threshold: 100,
|
|
14
|
+
enabled: true,
|
|
15
|
+
minSpeed: 300
|
|
16
|
+
}
|
|
17
|
+
export function swipeable(node, options = defaultOptions) {
|
|
18
|
+
let track = {}
|
|
19
|
+
let listeners = {}
|
|
23
20
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
startTime = new Date().getTime()
|
|
21
|
+
const updateListeners = (options) => {
|
|
22
|
+
removeListeners(node, listeners)
|
|
23
|
+
listeners = getListeners(node, options, track)
|
|
24
|
+
setupListeners(node, listeners, options)
|
|
29
25
|
}
|
|
30
26
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const distX = touch.clientX - startX
|
|
34
|
-
const distY = touch.clientY - startY
|
|
35
|
-
const duration = (new Date().getTime() - startTime) / 1000
|
|
36
|
-
const speed = Math.max(Math.abs(distX), Math.abs(distY)) / duration
|
|
37
|
-
|
|
38
|
-
if (horizontal && speed > minSpeed) {
|
|
39
|
-
if (Math.abs(distX) > Math.abs(distY) && Math.abs(distX) >= threshold) {
|
|
40
|
-
if (distX > 0 && distX / duration > minSpeed) {
|
|
41
|
-
node.dispatchEvent(new CustomEvent('swipeRight'))
|
|
42
|
-
} else {
|
|
43
|
-
node.dispatchEvent(new CustomEvent('swipeLeft'))
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
27
|
+
options = { ...defaultOptions, ...options }
|
|
28
|
+
updateListeners(options)
|
|
47
29
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
30
|
+
return {
|
|
31
|
+
update: (data) => {
|
|
32
|
+
options = { ...options, ...data }
|
|
33
|
+
updateListeners(options)
|
|
34
|
+
},
|
|
35
|
+
destroy() {
|
|
36
|
+
removeListeners(node, listeners)
|
|
56
37
|
}
|
|
57
38
|
}
|
|
39
|
+
}
|
|
58
40
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (!enabled && listening) {
|
|
68
|
-
node.removeEventListener('touchstart', touchStart)
|
|
69
|
-
node.removeEventListener('touchend', touchEnd)
|
|
70
|
-
node.removeEventListener('mousedown', touchStart)
|
|
71
|
-
node.removeEventListener('mouseup', touchEnd)
|
|
72
|
-
listening = false
|
|
73
|
-
}
|
|
41
|
+
function getListeners(node, options, track) {
|
|
42
|
+
if (!options.enabled) return {}
|
|
43
|
+
|
|
44
|
+
let listeners = {
|
|
45
|
+
touchend: (e) => touchEnd(e, node, options, track),
|
|
46
|
+
touchstart: (e) => touchStart(e, track),
|
|
47
|
+
mousedown: (e) => touchStart(e, track),
|
|
48
|
+
mouseup: (e) => touchEnd(e, node, options, track)
|
|
74
49
|
}
|
|
50
|
+
return listeners
|
|
51
|
+
}
|
|
75
52
|
|
|
76
|
-
|
|
53
|
+
function touchStart(event, track) {
|
|
54
|
+
const touch = event.touches ? event.touches[0] : event
|
|
55
|
+
track.startX = touch.clientX
|
|
56
|
+
track.startY = touch.clientY
|
|
57
|
+
track.startTime = new Date().getTime()
|
|
58
|
+
}
|
|
77
59
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
60
|
+
function touchEnd(event, node, options, track) {
|
|
61
|
+
const { horizontal, vertical, threshold, minSpeed } = options
|
|
62
|
+
const touch = event.changedTouches ? event.changedTouches[0] : event
|
|
63
|
+
const distX = touch.clientX - track.startX
|
|
64
|
+
const distY = touch.clientY - track.startY
|
|
65
|
+
const duration = (new Date().getTime() - track.startTime) / 1000
|
|
66
|
+
const speed = Math.max(Math.abs(distX), Math.abs(distY)) / duration
|
|
85
67
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
68
|
+
if (horizontal && speed > minSpeed) {
|
|
69
|
+
if (Math.abs(distX) > Math.abs(distY) && Math.abs(distX) >= threshold) {
|
|
70
|
+
if (distX > 0 && distX / duration > minSpeed) {
|
|
71
|
+
node.dispatchEvent(new CustomEvent('swipeRight'))
|
|
72
|
+
} else {
|
|
73
|
+
node.dispatchEvent(new CustomEvent('swipeLeft'))
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (vertical && speed > minSpeed) {
|
|
79
|
+
if (Math.abs(distY) > Math.abs(distX) && Math.abs(distY) >= threshold) {
|
|
80
|
+
if (distY > 0) {
|
|
81
|
+
node.dispatchEvent(new CustomEvent('swipeDown'))
|
|
82
|
+
} else {
|
|
83
|
+
node.dispatchEvent(new CustomEvent('swipeUp'))
|
|
84
|
+
}
|
|
90
85
|
}
|
|
91
86
|
}
|
|
92
87
|
}
|
package/src/traversable.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getClosestAncestorWithAttribute,
|
|
3
|
-
mapKeyboardEventsToActions
|
|
3
|
+
mapKeyboardEventsToActions,
|
|
4
|
+
setupListeners,
|
|
5
|
+
removeListeners,
|
|
6
|
+
emit
|
|
4
7
|
} from './lib'
|
|
5
8
|
|
|
6
9
|
const defaultOptions = {
|
|
@@ -8,22 +11,48 @@ const defaultOptions = {
|
|
|
8
11
|
nested: false,
|
|
9
12
|
enabled: true
|
|
10
13
|
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* An action that can be used to traverse a nested list of items using keyboard and mouse.
|
|
17
|
+
*
|
|
18
|
+
* @param {HTMLElement} element
|
|
19
|
+
* @param {import('./types').TraversableOptions} data
|
|
20
|
+
* @returns
|
|
21
|
+
*/
|
|
11
22
|
export function traversable(element, data) {
|
|
12
|
-
let listening = false
|
|
13
23
|
let options = {}
|
|
14
24
|
let tracker = {}
|
|
15
|
-
let handlers
|
|
25
|
+
let handlers
|
|
26
|
+
let actions
|
|
27
|
+
let listeners
|
|
16
28
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
29
|
+
const configure = (data) => {
|
|
30
|
+
options = { ...options, ...data }
|
|
31
|
+
|
|
32
|
+
removeListeners(element, listeners)
|
|
33
|
+
actions = getActions(element, tracker)
|
|
34
|
+
handlers = mapKeyboardEventsToActions(actions, options)
|
|
35
|
+
listeners = getListeners(handlers, actions, tracker)
|
|
36
|
+
setupListeners(element, listeners, options)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
configure({ ...defaultOptions, ...data })
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
update: configure,
|
|
43
|
+
destroy: () => removeListeners(element, listeners)
|
|
24
44
|
}
|
|
45
|
+
}
|
|
25
46
|
|
|
26
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Returns the listeners for the given handlers and actions.
|
|
49
|
+
*
|
|
50
|
+
* @param {import('./types').KeyboardActions} handlers
|
|
51
|
+
* @param {import('./types').ActionHandlers} actions
|
|
52
|
+
* @param {import('./types').PositionTracker} tracker
|
|
53
|
+
*/
|
|
54
|
+
function getListeners(handlers, actions, tracker) {
|
|
55
|
+
return {
|
|
27
56
|
keydown: (event) => {
|
|
28
57
|
const action = handlers[event.key]
|
|
29
58
|
if (action) action(event)
|
|
@@ -38,40 +67,24 @@ export function traversable(element, data) {
|
|
|
38
67
|
}
|
|
39
68
|
}
|
|
40
69
|
}
|
|
41
|
-
|
|
42
|
-
const configure = (data) => {
|
|
43
|
-
// const valueChanged = options.value !== data.value
|
|
44
|
-
options = { ...options, ...data }
|
|
45
|
-
|
|
46
|
-
listening = setupEventHandlers(element, options, listening, listeners)
|
|
47
|
-
handlers = mapKeyboardEventsToActions(actions, options)
|
|
48
|
-
// if (valueChanged) handleValueChange(element, options)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
configure({ ...defaultOptions, ...data })
|
|
52
|
-
|
|
53
|
-
return {
|
|
54
|
-
update: configure,
|
|
55
|
-
destroy: () => configure({ enabled: false })
|
|
56
|
-
}
|
|
57
70
|
}
|
|
58
71
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
)
|
|
72
|
+
/**
|
|
73
|
+
*
|
|
74
|
+
* @param {HTMLElement} element
|
|
75
|
+
* @param {import('./types').PositionTracker} tracker
|
|
76
|
+
* @returns {import('./types').ActionHandlers}
|
|
77
|
+
*/
|
|
78
|
+
function getActions(element, tracker) {
|
|
79
|
+
const actions = {
|
|
80
|
+
next: () => emit(element, 'move', tracker),
|
|
81
|
+
previous: () => emit(element, 'move', tracker),
|
|
82
|
+
select: () => emit(element, 'select', tracker),
|
|
83
|
+
escape: () => emit(element, 'escape', tracker),
|
|
84
|
+
collapse: () => emit(element, 'collapse', tracker),
|
|
85
|
+
expand: () => emit(element, 'expand', tracker)
|
|
70
86
|
}
|
|
71
|
-
return
|
|
87
|
+
return actions
|
|
72
88
|
}
|
|
73
89
|
|
|
74
|
-
function emit(element, event, tracker) {
|
|
75
|
-
element.dispatchEvent(new CustomEvent(event, { detail: tracker }))
|
|
76
|
-
}
|
|
77
90
|
// function handleValueChange(element, options) {}
|
package/src/types.js
CHANGED
|
@@ -51,3 +51,61 @@
|
|
|
51
51
|
* @property {number} threshold - Threshold for swipe
|
|
52
52
|
* @property {number} minSpeed - Minimum speed for swipe
|
|
53
53
|
*/
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @typedef TraversableOptions
|
|
57
|
+
* @property {boolean} horizontal - Traverse horizontally
|
|
58
|
+
* @property {boolean} nested - Traverse nested items
|
|
59
|
+
* @property {boolean} enabled - Enable traversal
|
|
60
|
+
* @property {string} value - Value to be used for traversal
|
|
61
|
+
* @property {Array<*>} items - An array containing the data set to traverse
|
|
62
|
+
* @property {Array<integer} [indices] - Indices of the items to be traversed
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @typedef PositionTracker
|
|
67
|
+
* @property {integer} index
|
|
68
|
+
* @property {integer} previousIndex
|
|
69
|
+
*/
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @typedef EventHandlers
|
|
73
|
+
* @property {function} [keydown]
|
|
74
|
+
* @property {function} [keyup]
|
|
75
|
+
* @property {function} [click]
|
|
76
|
+
* @property {function} [touchstart]
|
|
77
|
+
* @property {function} [touchmove]
|
|
78
|
+
* @property {function} [touchend]
|
|
79
|
+
* @property {function} [touchcancel]
|
|
80
|
+
* @property {function} [mousedown]
|
|
81
|
+
* @property {function} [mouseup]
|
|
82
|
+
* @property {function} [mousemove]
|
|
83
|
+
*/
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @typedef {Object} ActionHandlers
|
|
87
|
+
* @property {Function} [next]
|
|
88
|
+
* @property {Function} [previous]
|
|
89
|
+
* @property {Function} [select]
|
|
90
|
+
* @property {Function} [escape]
|
|
91
|
+
* @property {Function} [collapse]
|
|
92
|
+
* @property {Function} [expand]
|
|
93
|
+
*/
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @typedef {Object} NavigationOptions
|
|
97
|
+
* @property {Boolean} [horizontal]
|
|
98
|
+
* @property {Boolean} [nested]
|
|
99
|
+
* @property {Boolean} [enabled]
|
|
100
|
+
*/
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @typedef {Object} KeyboardActions
|
|
104
|
+
* @property {Function} [ArrowDown]
|
|
105
|
+
* @property {Function} [ArrowUp]
|
|
106
|
+
* @property {Function} [ArrowRight]
|
|
107
|
+
* @property {Function} [ArrowLeft]
|
|
108
|
+
* @property {Function} [Enter]
|
|
109
|
+
* @property {Function} [Escape]
|
|
110
|
+
* @property {Function} [" "]
|
|
111
|
+
*/
|