@rokkit/actions 1.0.0-next.100
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/LICENSE +21 -0
- package/README.md +1 -0
- package/package.json +56 -0
- package/src/delegate.js +34 -0
- package/src/dismissable.js +33 -0
- package/src/fillable.js +106 -0
- package/src/hierarchy.js +156 -0
- package/src/index.js +13 -0
- package/src/lib/constants.js +35 -0
- package/src/lib/event-manager.js +50 -0
- package/src/lib/index.js +5 -0
- package/src/lib/internal.js +185 -0
- package/src/lib/viewport.js +123 -0
- package/src/navigable.js +46 -0
- package/src/navigator.js +182 -0
- package/src/pannable.js +67 -0
- package/src/swipeable.js +150 -0
- package/src/switchable.js +52 -0
- package/src/themeable.js +42 -0
- package/src/traversable.js +385 -0
- package/src/types.js +132 -0
- package/src/utils.js +24 -0
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import { has } from 'ramda'
|
|
2
|
+
import { EventManager } from './lib'
|
|
3
|
+
|
|
4
|
+
const defaultConfig = {
|
|
5
|
+
allowDrag: false,
|
|
6
|
+
allowDrop: false,
|
|
7
|
+
pageSize: 10,
|
|
8
|
+
horizontal: false,
|
|
9
|
+
vertical: true,
|
|
10
|
+
multiselect: false
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A svelte action to add keyboard navigation to a list/tree/grid
|
|
15
|
+
*
|
|
16
|
+
* @param {HTMLElement} root - The DOM root node to add the action to
|
|
17
|
+
* @param {Object} config - The configuration object
|
|
18
|
+
* @param {Object} config.store - The store object with navigation methods
|
|
19
|
+
* @param {Object} config.options - The configuration options
|
|
20
|
+
* @param {number} config.options.pageSize - The number of items to move on page up/down
|
|
21
|
+
* @param {boolean} config.options.horizontal - The orientation of the list/tree
|
|
22
|
+
* @param {boolean} config.options.vertical - The orientation of the list/tree
|
|
23
|
+
*/
|
|
24
|
+
export function traversable(root, config) {
|
|
25
|
+
const manager = EventManager(root, {})
|
|
26
|
+
const events = config.store.events
|
|
27
|
+
|
|
28
|
+
const unsubscribe = events.subscribe((data) => {
|
|
29
|
+
if (data.length > 0) {
|
|
30
|
+
data.forEach(({ type, detail }) => root.dispatchEvent(new CustomEvent(type, { detail })))
|
|
31
|
+
events.set([])
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
updateEventHandlers(root, manager, config)
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
destroy: () => {
|
|
39
|
+
// console.log(typeof unsubscribe)
|
|
40
|
+
unsubscribe()
|
|
41
|
+
manager.reset()
|
|
42
|
+
},
|
|
43
|
+
update: (newConfig) => updateEventHandlers(root, manager, newConfig)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Update the event handlers based on the configuration
|
|
49
|
+
*
|
|
50
|
+
* @param {HTMLElement} root - The DOM root node to add the action to
|
|
51
|
+
* @param {Object} manager - The event manager object
|
|
52
|
+
* @param {Object} config - The configuration object
|
|
53
|
+
*/
|
|
54
|
+
function updateEventHandlers(root, manager, config) {
|
|
55
|
+
const store = config.store
|
|
56
|
+
const options = { ...defaultConfig, ...config.options }
|
|
57
|
+
|
|
58
|
+
const listeners = {
|
|
59
|
+
keydown: getKeydownHandler(store, options, root),
|
|
60
|
+
click: getClickHandler(store, options)
|
|
61
|
+
}
|
|
62
|
+
if (options.allowDrag) listeners.dragstart = getDragEventHandler(store, 'dragStart')
|
|
63
|
+
if (options.allowDrop) {
|
|
64
|
+
listeners.dragover = getDragEventHandler(store, 'dragOver')
|
|
65
|
+
listeners.drop = getDragEventHandler(store, 'dropOver')
|
|
66
|
+
}
|
|
67
|
+
manager.update(listeners)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get a map of actions for various key combinations
|
|
72
|
+
*
|
|
73
|
+
* @param {Object} store - The store object with navigation methods
|
|
74
|
+
* @param {number} pageSize - The number of items to move on page up/down
|
|
75
|
+
*/
|
|
76
|
+
function getKeyHandlers(store, options) {
|
|
77
|
+
const { pageSize, horizontal, vertical } = options
|
|
78
|
+
const isGrid = horizontal && vertical
|
|
79
|
+
const arrowActions = isGrid
|
|
80
|
+
? getArrowKeyActionsForGrid(store)
|
|
81
|
+
: getArrowKeyActions(store, horizontal)
|
|
82
|
+
|
|
83
|
+
const actions = {
|
|
84
|
+
...arrowActions,
|
|
85
|
+
PageUp: () => store.moveByOffset(-pageSize),
|
|
86
|
+
PageDown: () => store.moveByOffset(pageSize),
|
|
87
|
+
Home: () => store.moveFirst(),
|
|
88
|
+
End: () => store.moveLast(),
|
|
89
|
+
Enter: () => store.select(),
|
|
90
|
+
Escape: () => store.escape(),
|
|
91
|
+
' ': () => store.select()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const modifierActions = {
|
|
95
|
+
ctrl: getMetaKeyActions(store, horizontal),
|
|
96
|
+
meta: getMetaKeyActions(store, horizontal),
|
|
97
|
+
shift: isGrid ? getShiftKeyActionsForGrid(store) : getShiftKeyActions(store, horizontal)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return { actions, modifierActions }
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Get action handlers based on direction
|
|
105
|
+
*
|
|
106
|
+
* @param {Object} store - The store object with navigation methods
|
|
107
|
+
* @param {boolean} horizontal - if the content is navigable horizontally
|
|
108
|
+
*/
|
|
109
|
+
function getArrowKeyActions(store, horizontal = false) {
|
|
110
|
+
if (horizontal) {
|
|
111
|
+
return {
|
|
112
|
+
ArrowUp: () => store.collapse(),
|
|
113
|
+
ArrowDown: () => store.expand(),
|
|
114
|
+
ArrowRight: () => store.moveByOffset(1),
|
|
115
|
+
ArrowLeft: () => store.moveByOffset(-1)
|
|
116
|
+
}
|
|
117
|
+
} else {
|
|
118
|
+
return {
|
|
119
|
+
ArrowUp: () => store.moveByOffset(-1),
|
|
120
|
+
ArrowDown: () => store.moveByOffset(1),
|
|
121
|
+
ArrowRight: () => store.expand(),
|
|
122
|
+
ArrowLeft: () => store.collapse()
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Get the handler function for the keydown event
|
|
129
|
+
*
|
|
130
|
+
* @param {Object} store - The store object with navigation methods
|
|
131
|
+
* @param {Object} options - The configuration options
|
|
132
|
+
*/
|
|
133
|
+
function getClickHandler(store, options) {
|
|
134
|
+
const { multiselect = false } = options
|
|
135
|
+
|
|
136
|
+
function handleClick(event) {
|
|
137
|
+
const modifiers = identifyModifiers(event)
|
|
138
|
+
const indexPath = getTargetIndex(event)
|
|
139
|
+
|
|
140
|
+
if (!indexPath) return
|
|
141
|
+
event.stopPropagation()
|
|
142
|
+
|
|
143
|
+
if (isToggleStateIcon(event.target)) {
|
|
144
|
+
store.toggleExpansion(indexPath)
|
|
145
|
+
} else {
|
|
146
|
+
if (multiselect) {
|
|
147
|
+
handleMultiSelect(store, indexPath, modifiers)
|
|
148
|
+
} else {
|
|
149
|
+
store.moveTo(indexPath)
|
|
150
|
+
store.select(indexPath)
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// dispatchEvents(event.target, store)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return handleClick
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Get the handler function for the drag events
|
|
161
|
+
*
|
|
162
|
+
* @param {Object} store - The store object with navigation methods
|
|
163
|
+
* @param {string} eventName - The name of the event to dispatch
|
|
164
|
+
* @returns {Function} The event handler function
|
|
165
|
+
*/
|
|
166
|
+
function getDragEventHandler(store, eventName) {
|
|
167
|
+
function handle(event) {
|
|
168
|
+
const index = getTargetIndex(event)
|
|
169
|
+
if (index) store[eventName](index)
|
|
170
|
+
}
|
|
171
|
+
return handle
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Handle multi-select based on the modifier keys pressed
|
|
176
|
+
*
|
|
177
|
+
* @param {Object} store - The store object with navigation methods
|
|
178
|
+
* @param {number[]} index - The index path of the item to select
|
|
179
|
+
* @param {string[]} modifier - The modifier keys pressed
|
|
180
|
+
*/
|
|
181
|
+
function handleMultiSelect(store, index, modifier) {
|
|
182
|
+
if (modifier.includes('shift')) {
|
|
183
|
+
store.selectRange(index)
|
|
184
|
+
} else if (modifier.includes('ctrl') || modifier.includes('meta')) {
|
|
185
|
+
store.toggleSelection(index)
|
|
186
|
+
} else {
|
|
187
|
+
store.select(index)
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Get the keydown event handler
|
|
193
|
+
*
|
|
194
|
+
* @param {Object} store - The store object with navigation methods
|
|
195
|
+
* @param {Object} options - The configuration options
|
|
196
|
+
* @param {HTMLElement} root - The root element to add the event listener to
|
|
197
|
+
*/
|
|
198
|
+
function getKeydownHandler(store, options, root) {
|
|
199
|
+
const handlers = getKeyHandlers(store, options)
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Use the keyboard event map to handle the keydown event
|
|
203
|
+
*
|
|
204
|
+
* @param {KeyboardEvent} event - The keyboard event
|
|
205
|
+
*/
|
|
206
|
+
function handleKeydown(event) {
|
|
207
|
+
const action = getAction(event, handlers)
|
|
208
|
+
if (action) {
|
|
209
|
+
event.preventDefault()
|
|
210
|
+
action()
|
|
211
|
+
scrollIntoView(root, store)
|
|
212
|
+
// dispatchEvents(root, store)
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return handleKeydown
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Get the action for the keydown event
|
|
221
|
+
*
|
|
222
|
+
* @param {KeyboardEvent} event - The keyboard event
|
|
223
|
+
* @param {Object} handlers - The key handlers object
|
|
224
|
+
*/
|
|
225
|
+
function getAction(event, handlers) {
|
|
226
|
+
const key = event.key.length === 1 ? event.key.toUpperCase() : event.key
|
|
227
|
+
const modifier = identifyModifiers(event).join('-')
|
|
228
|
+
if (modifier.length === 0) return handlers.actions[key]
|
|
229
|
+
|
|
230
|
+
if (has(modifier, handlers.modifierActions)) {
|
|
231
|
+
return handlers.modifierActions[modifier][key]
|
|
232
|
+
}
|
|
233
|
+
return null
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Identify modifier keys pressed in the event
|
|
238
|
+
*
|
|
239
|
+
* @param {KeyboardEvent} event - The keyboard event
|
|
240
|
+
*/
|
|
241
|
+
function identifyModifiers(event) {
|
|
242
|
+
const modifiers = []
|
|
243
|
+
|
|
244
|
+
if (event.ctrlKey) modifiers.push('ctrl')
|
|
245
|
+
if (event.shiftKey) modifiers.push('shift')
|
|
246
|
+
if (event.metaKey) modifiers.push('meta')
|
|
247
|
+
|
|
248
|
+
return modifiers
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Get the meta key actions for a list/tree
|
|
253
|
+
*
|
|
254
|
+
* @param {Object} store - The store object with navigation methods
|
|
255
|
+
* @param {boolean} horizontal - The orientation of the list/tree
|
|
256
|
+
*/
|
|
257
|
+
function getMetaKeyActions(store, horizontal = false) {
|
|
258
|
+
const actions = {
|
|
259
|
+
X: () => store.cut(),
|
|
260
|
+
C: () => store.copy(),
|
|
261
|
+
V: () => store.paste(),
|
|
262
|
+
A: () => store.selectAll(),
|
|
263
|
+
D: () => store.selectNone(),
|
|
264
|
+
I: () => store.selectInvert(),
|
|
265
|
+
Z: () => store.undo(),
|
|
266
|
+
Y: () => store.redo()
|
|
267
|
+
}
|
|
268
|
+
const horizontalActions = {
|
|
269
|
+
ArrowRight: () => store.moveLast(),
|
|
270
|
+
ArrowLeft: () => store.moveFirst()
|
|
271
|
+
}
|
|
272
|
+
const verticalActions = {
|
|
273
|
+
ArrowUp: () => store.moveFirst(),
|
|
274
|
+
ArrowDown: () => store.moveLast()
|
|
275
|
+
}
|
|
276
|
+
const arrowActions = horizontal ? horizontalActions : verticalActions
|
|
277
|
+
|
|
278
|
+
return { ...actions, ...arrowActions }
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Get the shift key actions for a list
|
|
283
|
+
*
|
|
284
|
+
* @param {Object} store - The store object with navigation methods
|
|
285
|
+
* @param {boolean} horizontal - The orientation of the list/tree
|
|
286
|
+
*/
|
|
287
|
+
function getShiftKeyActions(store, horizontal = false) {
|
|
288
|
+
const actions = {
|
|
289
|
+
Home: () => store.selectRange(-Infinity),
|
|
290
|
+
End: () => store.selectRange(Infinity)
|
|
291
|
+
}
|
|
292
|
+
const horizontalActions = {
|
|
293
|
+
ArrowRight: () => store.selectRange(1),
|
|
294
|
+
ArrowLeft: () => store.selectRange(-1)
|
|
295
|
+
}
|
|
296
|
+
const verticalActions = {
|
|
297
|
+
ArrowUp: () => store.selectRange(-1),
|
|
298
|
+
ArrowDown: () => store.selectRange(1)
|
|
299
|
+
}
|
|
300
|
+
const arrowActions = horizontal ? horizontalActions : verticalActions
|
|
301
|
+
|
|
302
|
+
return { ...actions, ...arrowActions }
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Get the arrow key actions for a grid
|
|
307
|
+
*
|
|
308
|
+
* @param {Object} store - The store object with navigation methods
|
|
309
|
+
* @returns {Object} - The map of actions
|
|
310
|
+
*/
|
|
311
|
+
function getArrowKeyActionsForGrid(store) {
|
|
312
|
+
return {
|
|
313
|
+
ArrowUp: () => store.moveByOffset(-1),
|
|
314
|
+
ArrowDown: () => store.moveByOffset(1),
|
|
315
|
+
ArrowRight: () => store.moveByOffset(0, 1),
|
|
316
|
+
ArrowLeft: () => store.moveByOffset(0, -1),
|
|
317
|
+
Home: () => store.moveByOffset(-Infinity, -Infinity),
|
|
318
|
+
End: () => store.moveByOffset(Infinity, Infinity)
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Get the shift key actions for a grid
|
|
324
|
+
*
|
|
325
|
+
* @param {Object} store - The store object with navigation methods
|
|
326
|
+
* @returns {Object} - The map of actions
|
|
327
|
+
*/
|
|
328
|
+
function getShiftKeyActionsForGrid(store) {
|
|
329
|
+
return {
|
|
330
|
+
ArrowUp: () => store.selectRange(-1),
|
|
331
|
+
ArrowDown: () => store.selectRange(1),
|
|
332
|
+
ArrowRight: () => store.selectRange(0, 1),
|
|
333
|
+
ArrowLeft: () => store.selectRange(0, -1),
|
|
334
|
+
Home: () => store.selectRange(0, -Infinity),
|
|
335
|
+
End: () => store.selectRange(0, Infinity)
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Identify if an html element is a toggle state icon
|
|
341
|
+
* A toggle state icon element tag is ICON and has a data-state attribute value of 'opened' or 'closed'
|
|
342
|
+
*
|
|
343
|
+
* @param {HTMLElement} element - The html element to check
|
|
344
|
+
*/
|
|
345
|
+
function isToggleStateIcon(element) {
|
|
346
|
+
return (
|
|
347
|
+
element.tagName === 'ICON' && ['opened', 'closed'].includes(element.getAttribute('data-state'))
|
|
348
|
+
)
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Get the index of the target element
|
|
353
|
+
*
|
|
354
|
+
* @param {MouseEvent} event - The mouse event
|
|
355
|
+
*/
|
|
356
|
+
function getTargetIndex(event) {
|
|
357
|
+
const target = event.target.closest('[data-index]')
|
|
358
|
+
if (target) return target.getAttribute('data-index').split('-').map(Number)
|
|
359
|
+
|
|
360
|
+
return null
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Make the current item visible in the view
|
|
365
|
+
*
|
|
366
|
+
* @param {HTMLElement} root - The root element which contains the items
|
|
367
|
+
* @param {Object} store - The item to make visible
|
|
368
|
+
*/
|
|
369
|
+
function scrollIntoView(root, store) {
|
|
370
|
+
const item = store.currentItem()
|
|
371
|
+
const dataIndex = item.indexPath.join('-')
|
|
372
|
+
const node = root.querySelector(`[data-index="${dataIndex}"]`)
|
|
373
|
+
if (node) node.scrollIntoView({ behavior: 'smooth', block: 'nearest' })
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Dispatch custom events based on the state changes
|
|
378
|
+
*
|
|
379
|
+
* @param {HTMLElement} root - The root element to dispatch the events from
|
|
380
|
+
* @param {Object} store - The store object with navigation methods
|
|
381
|
+
*/
|
|
382
|
+
// function dispatchEvents(root, store) {
|
|
383
|
+
// const events = store.getEvents()
|
|
384
|
+
// events.forEach((event, detail) => root.dispatchEvent(new CustomEvent(event, { detail })))
|
|
385
|
+
// }
|
package/src/types.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef SvelteActionReturn
|
|
3
|
+
* @property {() => void} destroy
|
|
4
|
+
* @property {() => void} [update]
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef FillableData
|
|
9
|
+
* @property {string} value
|
|
10
|
+
* @property {integer} actualIndex
|
|
11
|
+
* @property {integer} expectedIndex
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @typedef FillOptions
|
|
16
|
+
* @property {Array<FillableData>} options available options to fill
|
|
17
|
+
* @property {integer} current index of option to be filled
|
|
18
|
+
* @property {boolean} check validate filled values
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* A part of the path to node in hierarchy
|
|
23
|
+
*
|
|
24
|
+
* @typedef PathFragment
|
|
25
|
+
* @property {integer} index - Index to item in array
|
|
26
|
+
* @property {Array<*>} items - Array of items
|
|
27
|
+
* @property {import('@rokkit/core').FieldMapping} fields - Field mapping for the data
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Options for the Navigable action
|
|
32
|
+
* @typedef NavigableOptions
|
|
33
|
+
* @property {boolean} horizontal - Navigate horizontally
|
|
34
|
+
* @property {boolean} nested - Navigate nested items
|
|
35
|
+
* @property {boolean} enabled - Enable navigation
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @typedef NavigatorOptions
|
|
40
|
+
* @property {Array<*>} items - An array containing the data set to navigate
|
|
41
|
+
* @property {boolean} [vertical=true] - Identifies whether navigation shoud be vertical or horizontal
|
|
42
|
+
* @property {string} [idPrefix='id-'] - id prefix used for identifying individual node
|
|
43
|
+
* @property {import('../constants').FieldMapping} fields - Field mapping to identify attributes to be used for state and identification of children
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @typedef SwipeableOptions
|
|
48
|
+
* @property {boolean} horizontal - Swipe horizontally
|
|
49
|
+
* @property {boolean} vertical - Swipe vertically
|
|
50
|
+
* @property {boolean} enabled - Enable swiping
|
|
51
|
+
* @property {number} threshold - Threshold for swipe
|
|
52
|
+
* @property {number} minSpeed - Minimum speed for swipe
|
|
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
|
+
*/
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @typedef {Object} TouchTracker
|
|
115
|
+
* @property {number} startX - The start X position of the touch.
|
|
116
|
+
* @property {number} startY - The start Y position of the touch.
|
|
117
|
+
* @property {number} startTime - The start time of the touch.
|
|
118
|
+
*/
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @typedef {Object} PushDownOptions
|
|
122
|
+
* @property {string} selector - The CSS selector for the child element to which keyboard events will be forwarded.
|
|
123
|
+
* @property {Array<string>} [events=['keydown', 'keyup', 'keypress']] - The keyboard events to forward.
|
|
124
|
+
*/
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* @typedef {Object} Bounds
|
|
128
|
+
* @property {number} lower
|
|
129
|
+
* @property {number} upper
|
|
130
|
+
*/
|
|
131
|
+
|
|
132
|
+
export default {}
|
package/src/utils.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export function handleAction(actions, event) {
|
|
2
|
+
if (event.key in actions) {
|
|
3
|
+
event.preventDefault()
|
|
4
|
+
event.stopPropagation()
|
|
5
|
+
actions[event.key]()
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function getKeyboardActions(node, options, handlers) {
|
|
10
|
+
const movement = options.horizontal
|
|
11
|
+
? { ArrowLeft: handlers.previous, ArrowRight: handlers.next }
|
|
12
|
+
: { ArrowUp: handlers.previous, ArrowDown: handlers.next }
|
|
13
|
+
const change = options.nested
|
|
14
|
+
? options.horizontal
|
|
15
|
+
? { ArrowUp: handlers.collapse, ArrowDown: handlers.expand }
|
|
16
|
+
: { ArrowLeft: handlers.collapse, ArrowRight: handlers.expand }
|
|
17
|
+
: {}
|
|
18
|
+
return {
|
|
19
|
+
Enter: handlers.select,
|
|
20
|
+
' ': handlers.select,
|
|
21
|
+
...movement,
|
|
22
|
+
...change
|
|
23
|
+
}
|
|
24
|
+
}
|