@rokkit/actions 1.0.0-next.100 → 1.0.0-next.103
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 +1 -1
- package/README.md +57 -1
- package/package.json +7 -26
- package/src/index.js +2 -12
- package/src/keyboard.svelte.js +58 -0
- package/src/types.js +5 -127
- package/src/utils.js +25 -21
- package/src/delegate.js +0 -34
- package/src/dismissable.js +0 -33
- package/src/fillable.js +0 -106
- package/src/hierarchy.js +0 -156
- package/src/lib/constants.js +0 -35
- package/src/lib/event-manager.js +0 -50
- package/src/lib/index.js +0 -5
- package/src/lib/internal.js +0 -185
- package/src/lib/viewport.js +0 -123
- package/src/navigable.js +0 -46
- package/src/navigator.js +0 -182
- package/src/pannable.js +0 -67
- package/src/swipeable.js +0 -150
- package/src/switchable.js +0 -52
- package/src/themeable.js +0 -42
- package/src/traversable.js +0 -385
package/src/traversable.js
DELETED
|
@@ -1,385 +0,0 @@
|
|
|
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
|
-
// }
|