@rokkit/actions 1.0.0-next.112 → 1.0.0-next.114
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 +1 -1
- package/src/kbd.js +185 -0
- package/src/navigable.svelte.js +12 -3
- package/src/navigator.svelte.js +3 -4
- package/src/utils.js +35 -103
package/package.json
CHANGED
package/src/kbd.js
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gets horizontal movement actions based on text direction
|
|
3
|
+
* @param {Object} handlers - Handler functions
|
|
4
|
+
* @param {string} [dir='ltr'] - Text direction ('ltr' or 'rtl')
|
|
5
|
+
* @returns {Object} Object mapping arrow keys to movement handlers
|
|
6
|
+
*/
|
|
7
|
+
function getHorizontalMovementActions(handlers, dir = 'ltr') {
|
|
8
|
+
return dir === 'rtl'
|
|
9
|
+
? { ArrowRight: handlers.previous, ArrowLeft: handlers.next }
|
|
10
|
+
: { ArrowLeft: handlers.previous, ArrowRight: handlers.next }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Gets vertical movement actions (not affected by direction)
|
|
15
|
+
* @param {Object} handlers - Handler functions
|
|
16
|
+
* @returns {Object} Object mapping arrow keys to movement handlers
|
|
17
|
+
*/
|
|
18
|
+
function getVerticalMovementActions(handlers) {
|
|
19
|
+
return { ArrowUp: handlers.previous, ArrowDown: handlers.next }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Gets horizontal expand/collapse actions (not affected by direction)
|
|
24
|
+
* @param {Object} handlers - Handler functions
|
|
25
|
+
* @returns {Object} Object mapping arrow keys to expand/collapse handlers
|
|
26
|
+
*/
|
|
27
|
+
function getHorizontalExpandActions(handlers) {
|
|
28
|
+
return { ArrowUp: handlers.collapse, ArrowDown: handlers.expand }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Gets vertical expand/collapse actions based on text direction
|
|
33
|
+
* @param {Object} handlers - Handler functions
|
|
34
|
+
* @param {string} [dir='ltr'] - Text direction ('ltr' or 'rtl')
|
|
35
|
+
* @returns {Object} Object mapping arrow keys to expand/collapse handlers
|
|
36
|
+
*/
|
|
37
|
+
function getVerticalExpandActions(handlers, dir = 'ltr') {
|
|
38
|
+
return dir === 'rtl'
|
|
39
|
+
? { ArrowRight: handlers.collapse, ArrowLeft: handlers.expand }
|
|
40
|
+
: { ArrowLeft: handlers.collapse, ArrowRight: handlers.expand }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Gets common selection actions
|
|
45
|
+
* @param {Object} handlers - Handler functions
|
|
46
|
+
* @returns {Object} Object mapping keys to selection handlers
|
|
47
|
+
*/
|
|
48
|
+
function getCommonActions(handlers) {
|
|
49
|
+
return {
|
|
50
|
+
Enter: handlers.select,
|
|
51
|
+
' ': handlers.select
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Default navigation options
|
|
56
|
+
export const defaultNavigationOptions = {
|
|
57
|
+
orientation: 'vertical',
|
|
58
|
+
dir: 'ltr',
|
|
59
|
+
nested: false,
|
|
60
|
+
enabled: true
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Gets keyboard action handlers based on orientation and direction
|
|
65
|
+
* @param {Object} options - Configuration options
|
|
66
|
+
* @param {Object} handlers - Event handler functions
|
|
67
|
+
* @returns {Object} Object mapping key presses to handler functions
|
|
68
|
+
*/
|
|
69
|
+
export function getKeyboardActions(options, handlers) {
|
|
70
|
+
const { orientation, dir, nested, enabled } = { ...defaultNavigationOptions, ...options }
|
|
71
|
+
|
|
72
|
+
if (!enabled) return {}
|
|
73
|
+
|
|
74
|
+
const common = getCommonActions(handlers)
|
|
75
|
+
|
|
76
|
+
// Determine movement actions based on orientation
|
|
77
|
+
const isHorizontal = orientation === 'horizontal'
|
|
78
|
+
const movement = isHorizontal
|
|
79
|
+
? getHorizontalMovementActions(handlers, dir)
|
|
80
|
+
: getVerticalMovementActions(handlers)
|
|
81
|
+
|
|
82
|
+
// If not nested, we don't need expand/collapse actions
|
|
83
|
+
if (!nested) {
|
|
84
|
+
return { ...common, ...movement }
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Determine expand/collapse actions based on orientation
|
|
88
|
+
const expandCollapse = isHorizontal
|
|
89
|
+
? getHorizontalExpandActions(handlers)
|
|
90
|
+
: getVerticalExpandActions(handlers, dir)
|
|
91
|
+
|
|
92
|
+
return { ...common, ...movement, ...expandCollapse }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Creates a keyboard action mapping based on navigation options
|
|
97
|
+
*
|
|
98
|
+
* @param {Object} options - Navigation options
|
|
99
|
+
* @param {string} options.orientation - Whether navigation is horizontal or vertical
|
|
100
|
+
* @param {string} options.dir - Text direction ('ltr' or 'rtl')
|
|
101
|
+
* @param {boolean} options.nested - Whether navigation is nested
|
|
102
|
+
* @returns {Object} Mapping of keys to actions
|
|
103
|
+
*/
|
|
104
|
+
export function createKeyboardActionMap(options) {
|
|
105
|
+
const { orientation, dir, nested } = options
|
|
106
|
+
const isHorizontal = orientation === 'horizontal'
|
|
107
|
+
|
|
108
|
+
// Define movement actions based on orientation and direction
|
|
109
|
+
let movementActions = {}
|
|
110
|
+
if (isHorizontal) {
|
|
111
|
+
movementActions = dir === 'rtl'
|
|
112
|
+
? { ArrowRight: 'previous', ArrowLeft: 'next' }
|
|
113
|
+
: { ArrowLeft: 'previous', ArrowRight: 'next' }
|
|
114
|
+
} else {
|
|
115
|
+
movementActions = { ArrowUp: 'previous', ArrowDown: 'next' }
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Define expand/collapse actions for nested option
|
|
119
|
+
let nestedActions = {}
|
|
120
|
+
if (nested) {
|
|
121
|
+
if (isHorizontal) {
|
|
122
|
+
nestedActions = { ArrowUp: 'collapse', ArrowDown: 'expand' }
|
|
123
|
+
} else {
|
|
124
|
+
nestedActions = dir === 'rtl'
|
|
125
|
+
? { ArrowRight: 'collapse', ArrowLeft: 'expand' }
|
|
126
|
+
: { ArrowLeft: 'collapse', ArrowRight: 'expand' }
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Common actions regardless of options
|
|
131
|
+
const commonActions = {
|
|
132
|
+
Enter: 'select',
|
|
133
|
+
' ': 'select',
|
|
134
|
+
Home: 'first',
|
|
135
|
+
End: 'last'
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Combine all possible actions
|
|
139
|
+
return {
|
|
140
|
+
...commonActions,
|
|
141
|
+
...movementActions,
|
|
142
|
+
...nestedActions
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Creates a keyboard action mapping for modifier keys based on navigation options
|
|
148
|
+
*
|
|
149
|
+
* @param {Object} options - Navigation options
|
|
150
|
+
* @param {string} options.orientation - Whether navigation is horizontal or vertical
|
|
151
|
+
* @returns {Object} Mapping of keys to actions
|
|
152
|
+
*/
|
|
153
|
+
export function createModifierKeyboardActionMap(options) {
|
|
154
|
+
const isHorizontal = options.orientation === 'horizontal'
|
|
155
|
+
const common = { ' ': 'extend', Home: 'first', End: 'last' }
|
|
156
|
+
const directional = isHorizontal
|
|
157
|
+
? { ArrowLeft: 'first', ArrowRight: 'last' }
|
|
158
|
+
: { ArrowUp: 'first', ArrowDown: 'last' }
|
|
159
|
+
return { ...common, ...directional }
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Gets the keyboard action for a key event
|
|
164
|
+
* @param {KeyboardEvent} event - The keyboard event
|
|
165
|
+
* @param {Object} options - Configuration options
|
|
166
|
+
* @returns {string|null} The action to perform, or null if no action is defined
|
|
167
|
+
*/
|
|
168
|
+
export function getKeyboardAction(event, options = {}) {
|
|
169
|
+
const { key, ctrlKey, metaKey } = event
|
|
170
|
+
|
|
171
|
+
// Use updated options with defaults
|
|
172
|
+
const mergedOptions = { ...defaultNavigationOptions, ...options }
|
|
173
|
+
|
|
174
|
+
// Check for modifier keys first (highest priority)
|
|
175
|
+
if (ctrlKey || metaKey) {
|
|
176
|
+
const modifierMap = createModifierKeyboardActionMap(mergedOptions)
|
|
177
|
+
return modifierMap[key] || null
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Get the action map based on options
|
|
181
|
+
const actionMap = createKeyboardActionMap(mergedOptions)
|
|
182
|
+
|
|
183
|
+
// Return the action or null if no matching key
|
|
184
|
+
return actionMap[key] || null
|
|
185
|
+
}
|
package/src/navigable.svelte.js
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { on } from 'svelte/events'
|
|
2
|
-
import {
|
|
2
|
+
import { getKeyboardActions, defaultNavigationOptions } from './kbd'
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
// Handle keyboard events
|
|
5
|
+
function handleAction(actions, event) {
|
|
6
|
+
if (event.key in actions) {
|
|
7
|
+
event.preventDefault()
|
|
8
|
+
event.stopPropagation()
|
|
9
|
+
actions[event.key]()
|
|
10
|
+
}
|
|
11
|
+
}
|
|
5
12
|
/**
|
|
6
13
|
* A svelte action function that captures keyboard evvents and emits event for corresponding movements.
|
|
7
14
|
*
|
|
@@ -22,8 +29,10 @@ export function navigable(node, options) {
|
|
|
22
29
|
const handleKeydown = (event) => handleAction(actions, event)
|
|
23
30
|
|
|
24
31
|
$effect(() => {
|
|
25
|
-
|
|
32
|
+
// Use defaultNavigationOptions from kbd.js
|
|
33
|
+
const props = { ...defaultNavigationOptions, ...options }
|
|
26
34
|
actions = getKeyboardActions(props, handlers)
|
|
35
|
+
|
|
27
36
|
const cleanup = on(node, 'keyup', handleKeydown)
|
|
28
37
|
|
|
29
38
|
return () => cleanup()
|
package/src/navigator.svelte.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { on } from 'svelte/events'
|
|
2
2
|
import { omit } from 'ramda'
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
const defaultOptions = { horizontal: false, nested: false, enabled: true }
|
|
3
|
+
import { getKeyboardAction, defaultNavigationOptions } from './kbd'
|
|
4
|
+
import { getClickAction, getPathFromEvent } from './utils'
|
|
6
5
|
|
|
7
6
|
const EVENT_MAP = {
|
|
8
7
|
first: ['move'],
|
|
@@ -79,7 +78,7 @@ function getHandlers(wrapper) {
|
|
|
79
78
|
*/
|
|
80
79
|
export function navigator(node, options) {
|
|
81
80
|
const { wrapper } = options
|
|
82
|
-
const config = { ...
|
|
81
|
+
const config = { ...defaultNavigationOptions, ...omit(['wrapper'], options) }
|
|
83
82
|
const handlers = getHandlers(wrapper)
|
|
84
83
|
|
|
85
84
|
const handleKeydown = (event) => {
|
package/src/utils.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { find, toPairs } from 'ramda'
|
|
2
2
|
|
|
3
|
+
const defaultNavigationOptions = {
|
|
4
|
+
orientation: 'vertical',
|
|
5
|
+
dir: 'ltr',
|
|
6
|
+
nested: false,
|
|
7
|
+
enabled: true
|
|
8
|
+
}
|
|
3
9
|
/**
|
|
4
10
|
* Finds the closest ancestor of the given element that has the given attribute.
|
|
5
11
|
*
|
|
@@ -42,30 +48,31 @@ export function handleAction(actions, event) {
|
|
|
42
48
|
}
|
|
43
49
|
}
|
|
44
50
|
|
|
45
|
-
/**
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
export function getKeyboardActions(options, handlers) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
51
|
+
// /**
|
|
52
|
+
// * Maps keys to actions based on the configuration.
|
|
53
|
+
// *
|
|
54
|
+
// * @param {import('./types').NavigableOptions} options
|
|
55
|
+
// * @param {import('./types').NavigableHandlers} handlers
|
|
56
|
+
// */
|
|
57
|
+
// export function getKeyboardActions(options, handlers) {
|
|
58
|
+
// const { horizontal, nested } = options
|
|
59
|
+
// if (!options.enabled) return {}
|
|
60
|
+
|
|
61
|
+
// const common = {
|
|
62
|
+
// Enter: handlers.select,
|
|
63
|
+
// ' ': handlers.select
|
|
64
|
+
// }
|
|
65
|
+
// const movement = horizontal
|
|
66
|
+
// ? { ArrowLeft: handlers.previous, ArrowRight: handlers.next }
|
|
67
|
+
// : { ArrowUp: handlers.previous, ArrowDown: handlers.next }
|
|
68
|
+
// const change = horizontal
|
|
69
|
+
// ? { ArrowUp: handlers.collapse, ArrowDown: handlers.expand }
|
|
70
|
+
// : { ArrowLeft: handlers.collapse, ArrowRight: handlers.expand }
|
|
71
|
+
|
|
72
|
+
// if (nested) return { ...common, ...movement, ...change }
|
|
73
|
+
// return { ...common, ...movement }
|
|
74
|
+
// }
|
|
75
|
+
// Keyboard related functions moved to kbd.js
|
|
69
76
|
|
|
70
77
|
/**
|
|
71
78
|
* Finds and returns an index path based on data-path attribute
|
|
@@ -79,61 +86,9 @@ export function getPathFromEvent(event) {
|
|
|
79
86
|
// return node ? getPathFromKey(node.getAttribute('data-path')) : null
|
|
80
87
|
}
|
|
81
88
|
|
|
82
|
-
|
|
83
|
-
* Creates a keyboard action mapping based on navigation options
|
|
84
|
-
*
|
|
85
|
-
* @param {Object} options - Navigation options
|
|
86
|
-
* @param {boolean} options.horizontal - Whether navigation is horizontal
|
|
87
|
-
* @param {boolean} options.nested - Whether navigation is nested
|
|
88
|
-
* @returns {Object} Mapping of keys to actions
|
|
89
|
-
*/
|
|
90
|
-
function createKeyboardActionMap(options) {
|
|
91
|
-
const { horizontal, nested } = options
|
|
92
|
-
|
|
93
|
-
// Define movement actions based on horizontal option
|
|
94
|
-
const movementActions = horizontal
|
|
95
|
-
? { ArrowLeft: 'previous', ArrowRight: 'next' }
|
|
96
|
-
: { ArrowUp: 'previous', ArrowDown: 'next' }
|
|
97
|
-
|
|
98
|
-
// Define expand/collapse actions for nested option
|
|
99
|
-
const nestedActions = nested
|
|
100
|
-
? horizontal
|
|
101
|
-
? { ArrowUp: 'collapse', ArrowDown: 'expand' }
|
|
102
|
-
: { ArrowLeft: 'collapse', ArrowRight: 'expand' }
|
|
103
|
-
: {}
|
|
104
|
-
|
|
105
|
-
// Common actions regardless of options
|
|
106
|
-
const commonActions = {
|
|
107
|
-
Enter: 'select',
|
|
108
|
-
' ': 'select',
|
|
109
|
-
Home: 'first',
|
|
110
|
-
End: 'last'
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Combine all possible actions
|
|
114
|
-
return {
|
|
115
|
-
...commonActions,
|
|
116
|
-
...movementActions,
|
|
117
|
-
...nestedActions
|
|
118
|
-
}
|
|
119
|
-
}
|
|
89
|
+
// createKeyboardActionMap moved to kbd.js
|
|
120
90
|
|
|
121
|
-
|
|
122
|
-
* Creates a keyboard action mapping based on navigation options
|
|
123
|
-
*
|
|
124
|
-
* @param {Object} options - Navigation options
|
|
125
|
-
* @param {boolean} options.horizontal - Whether navigation is horizontal
|
|
126
|
-
* @param {boolean} options.nested - Whether navigation is nested
|
|
127
|
-
* @returns {Object} Mapping of keys to actions
|
|
128
|
-
*/
|
|
129
|
-
function createModifierKeyboardActionMap(options) {
|
|
130
|
-
const { horizontal } = options
|
|
131
|
-
const common = { ' ': 'extend', Home: 'first', End: 'last' }
|
|
132
|
-
const directional = horizontal
|
|
133
|
-
? { ArrowLeft: 'first', ArrowRight: 'last' }
|
|
134
|
-
: { ArrowUp: 'first', ArrowDown: 'last' }
|
|
135
|
-
return { ...common, ...directional }
|
|
136
|
-
}
|
|
91
|
+
// createModifierKeyboardActionMap moved to kbd.js
|
|
137
92
|
/**
|
|
138
93
|
* Identifies if an element is a collapsible icon
|
|
139
94
|
* @param {HTMLElement} target
|
|
@@ -142,34 +97,11 @@ function createModifierKeyboardActionMap(options) {
|
|
|
142
97
|
function isNodeToggle(target) {
|
|
143
98
|
return (
|
|
144
99
|
target &&
|
|
145
|
-
target.
|
|
100
|
+
target.hasAttribute('data-tag-icon') &&
|
|
146
101
|
['closed', 'opened'].includes(target.getAttribute('data-state'))
|
|
147
102
|
)
|
|
148
103
|
}
|
|
149
|
-
|
|
150
|
-
* Determines an action based on a keyboard event and navigation options
|
|
151
|
-
*
|
|
152
|
-
* @param {KeyboardEvent} event - The keyboard event
|
|
153
|
-
* @param {Object} options - Navigation options
|
|
154
|
-
* @param {boolean} options.horizontal - Whether navigation is horizontal
|
|
155
|
-
* @param {boolean} options.nested - Whether navigation is nested
|
|
156
|
-
* @returns {string|null} The determined action or null if no action matches
|
|
157
|
-
*/
|
|
158
|
-
export const getKeyboardAction = (event, options) => {
|
|
159
|
-
const { key, ctrlKey, metaKey } = event
|
|
160
|
-
|
|
161
|
-
// Check for modifier keys first (highest priority)
|
|
162
|
-
if (ctrlKey || metaKey) {
|
|
163
|
-
const modifierMap = createModifierKeyboardActionMap(options)
|
|
164
|
-
return modifierMap[key] || null
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Get the action map based on options
|
|
168
|
-
const actionMap = createKeyboardActionMap(options)
|
|
169
|
-
|
|
170
|
-
// Return the action or null if no matching key
|
|
171
|
-
return actionMap[key] || null
|
|
172
|
-
}
|
|
104
|
+
// getKeyboardAction moved to kbd.js
|
|
173
105
|
|
|
174
106
|
/**
|
|
175
107
|
* Determines an action based on a click event
|