@rokkit/actions 1.0.0-next.133 → 1.0.0-next.134
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/README.md +166 -34
- package/package.json +2 -1
- package/src/hover-lift.svelte.js +1 -2
- package/src/magnetic.svelte.js +1 -2
- package/src/navigator.js +1 -3
- package/src/navigator.svelte.js +1 -2
- package/src/reveal.svelte.js +1 -2
- package/src/ripple.svelte.js +1 -2
- package/src/trigger.js +3 -1
- package/src/utils.js +0 -1
package/README.md
CHANGED
|
@@ -1,57 +1,189 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @rokkit/actions
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Svelte actions and DOM utilities for Rokkit components.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Installation
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
```bash
|
|
8
|
+
npm install @rokkit/actions
|
|
9
|
+
# or
|
|
10
|
+
bun add @rokkit/actions
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Overview
|
|
14
|
+
|
|
15
|
+
`@rokkit/actions` provides the DOM event layer that `@rokkit/ui` components build on. The main export is `Navigator` — a class that wires keyboard, click, and focus events on a container element to a `Wrapper` or `ListController` instance. The package also includes standalone Svelte `use:` actions for effects like ripple, magnetic snap, reveal animations, dismissal on click-outside, and declarative keyboard shortcuts.
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
### navigator — keyboard and click navigation
|
|
20
|
+
|
|
21
|
+
The `navigator` action connects a container element's DOM events to a Wrapper controller. It handles `ArrowUp`/`ArrowDown`/`ArrowLeft`/`ArrowRight`, `Home`, `End`, `Enter`, `Space`, typeahead, and click-to-select.
|
|
22
|
+
|
|
23
|
+
```svelte
|
|
24
|
+
<script>
|
|
25
|
+
import { navigator } from '@rokkit/actions'
|
|
26
|
+
import { ProxyTree, Wrapper } from '@rokkit/states'
|
|
27
|
+
|
|
28
|
+
const tree = new ProxyTree(items)
|
|
29
|
+
const wrapper = new Wrapper(tree, { onselect })
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<ul use:navigator={{ controller: wrapper, orientation: 'vertical' }}>
|
|
33
|
+
{#each wrapper.flatView as node (node.key)}
|
|
34
|
+
<li data-path={node.key}>{node.proxy.label}</li>
|
|
35
|
+
{/each}
|
|
36
|
+
</ul>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The `data-path` attribute on each item is required — Navigator uses it to resolve which item was clicked or focused.
|
|
40
|
+
|
|
41
|
+
### Navigator class (imperative usage)
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
import { Navigator } from '@rokkit/actions'
|
|
45
|
+
|
|
46
|
+
const nav = new Navigator(containerEl, wrapper, {
|
|
47
|
+
orientation: 'vertical', // 'vertical' | 'horizontal'
|
|
48
|
+
collapsible: true // enable expand/collapse key handling
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
// Clean up when done
|
|
52
|
+
nav.destroy()
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### keyboard — declarative shortcut binding
|
|
56
|
+
|
|
57
|
+
```svelte
|
|
58
|
+
<script>
|
|
59
|
+
import { keyboard } from '@rokkit/actions'
|
|
60
|
+
</script>
|
|
61
|
+
|
|
62
|
+
<div
|
|
63
|
+
use:keyboard={{ submit: 'enter', cancel: 'escape' }}
|
|
64
|
+
onsubmit={() => save()}
|
|
65
|
+
oncancel={() => close()}
|
|
66
|
+
>
|
|
67
|
+
...
|
|
68
|
+
</div>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Default mappings: alphabet keys dispatch `add`, Enter dispatches `submit`, Escape dispatches `cancel`, Backspace/Delete dispatch `delete`.
|
|
72
|
+
|
|
73
|
+
### ripple — click ripple effect
|
|
74
|
+
|
|
75
|
+
```svelte
|
|
76
|
+
<script>
|
|
77
|
+
import { ripple } from '@rokkit/actions'
|
|
78
|
+
</script>
|
|
79
|
+
|
|
80
|
+
<button use:ripple>Click me</button>
|
|
81
|
+
|
|
82
|
+
<!-- With options -->
|
|
83
|
+
<button use:ripple={{ color: 'white', opacity: 0.2, duration: 400 }}>Click me</button>
|
|
84
|
+
```
|
|
8
85
|
|
|
9
|
-
|
|
86
|
+
### hoverLift — elevation shadow on hover
|
|
10
87
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
88
|
+
```svelte
|
|
89
|
+
<script>
|
|
90
|
+
import { hoverLift } from '@rokkit/actions'
|
|
91
|
+
</script>
|
|
92
|
+
|
|
93
|
+
<div use:hoverLift>Card content</div>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### magnetic — snap-to-cursor effect
|
|
97
|
+
|
|
98
|
+
```svelte
|
|
99
|
+
<script>
|
|
100
|
+
import { magnetic } from '@rokkit/actions'
|
|
101
|
+
</script>
|
|
17
102
|
|
|
18
|
-
|
|
103
|
+
<button use:magnetic>Hover me</button>
|
|
104
|
+
```
|
|
19
105
|
|
|
20
|
-
|
|
21
|
-
- _submit_: enter causes a `submit` event
|
|
22
|
-
- _cancel_: escape causes a `cancel` event
|
|
23
|
-
- _delete_: backspace or delete causes a `delete` event
|
|
106
|
+
### reveal — intersection observer reveal animation
|
|
24
107
|
|
|
25
|
-
|
|
108
|
+
```svelte
|
|
109
|
+
<script>
|
|
110
|
+
import { reveal } from '@rokkit/actions'
|
|
111
|
+
</script>
|
|
112
|
+
|
|
113
|
+
<section use:reveal>Fades in when scrolled into view</section>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### dismissable — click-outside dismissal
|
|
26
117
|
|
|
27
118
|
```svelte
|
|
28
119
|
<script>
|
|
29
|
-
import {
|
|
120
|
+
import { dismissable } from '@rokkit/actions'
|
|
30
121
|
|
|
31
|
-
|
|
32
|
-
console.log(`${event.detail} pressed`)
|
|
33
|
-
}
|
|
122
|
+
let open = $state(false)
|
|
34
123
|
</script>
|
|
35
124
|
|
|
36
|
-
<div use:
|
|
125
|
+
<div use:dismissable={{ enabled: open, ondismiss: () => (open = false) }}>Dropdown content</div>
|
|
37
126
|
```
|
|
38
127
|
|
|
39
|
-
###
|
|
128
|
+
### swipeable — touch swipe detection
|
|
40
129
|
|
|
41
130
|
```svelte
|
|
42
131
|
<script>
|
|
43
|
-
import {
|
|
44
|
-
|
|
45
|
-
console.log(`${event.detail} pressed`)
|
|
46
|
-
}
|
|
132
|
+
import { swipeable } from '@rokkit/actions'
|
|
133
|
+
</script>
|
|
47
134
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
135
|
+
<div use:swipeable onswipeleft={() => next()} onswiperight={() => prev()}>Swipeable content</div>
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### themable — apply theme CSS variables
|
|
139
|
+
|
|
140
|
+
```svelte
|
|
141
|
+
<script>
|
|
142
|
+
import { themable } from '@rokkit/actions'
|
|
54
143
|
</script>
|
|
55
144
|
|
|
56
|
-
<div use:
|
|
145
|
+
<div use:themable={{ theme: 'ocean' }}>Themed content</div>
|
|
57
146
|
```
|
|
147
|
+
|
|
148
|
+
## API Reference
|
|
149
|
+
|
|
150
|
+
### Navigator options
|
|
151
|
+
|
|
152
|
+
| Option | Type | Default | Description |
|
|
153
|
+
| ------------- | ---------------------------- | ------------ | ------------------------------------- |
|
|
154
|
+
| `orientation` | `'vertical' \| 'horizontal'` | `'vertical'` | Arrow key axis for prev/next movement |
|
|
155
|
+
| `collapsible` | `boolean` | `false` | Enable expand/collapse via arrow keys |
|
|
156
|
+
|
|
157
|
+
### buildKeymap / resolveAction
|
|
158
|
+
|
|
159
|
+
Low-level utilities for constructing custom keymaps:
|
|
160
|
+
|
|
161
|
+
```js
|
|
162
|
+
import { buildKeymap, resolveAction, ACTIONS } from '@rokkit/actions'
|
|
163
|
+
|
|
164
|
+
const keymap = buildKeymap({ orientation: 'vertical', collapsible: true })
|
|
165
|
+
const action = resolveAction(keymap, event) // returns action string or null
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Exports
|
|
169
|
+
|
|
170
|
+
| Export | Type | Description |
|
|
171
|
+
| --------------- | ------------- | ---------------------------------------------- |
|
|
172
|
+
| `Navigator` | Class | DOM event wiring for Wrapper/ListController |
|
|
173
|
+
| `navigator` | Svelte action | `use:navigator` wrapper around Navigator class |
|
|
174
|
+
| `keyboard` | Svelte action | Declarative keyboard shortcut binding |
|
|
175
|
+
| `ripple` | Svelte action | Material Design ink ripple on click |
|
|
176
|
+
| `hoverLift` | Svelte action | Elevation shadow on hover |
|
|
177
|
+
| `magnetic` | Svelte action | Snap-to-cursor magnetic effect |
|
|
178
|
+
| `reveal` | Svelte action | Intersection-observer reveal animation |
|
|
179
|
+
| `dismissable` | Svelte action | Click-outside dismissal |
|
|
180
|
+
| `pannable` | Svelte action | Pan / drag detection |
|
|
181
|
+
| `swipeable` | Svelte action | Touch swipe detection |
|
|
182
|
+
| `themable` | Svelte action | Apply theme CSS vars to element |
|
|
183
|
+
| `buildKeymap` | Function | Build a keymap for given orientation/options |
|
|
184
|
+
| `resolveAction` | Function | Resolve a keyboard event to an action string |
|
|
185
|
+
| `ACTIONS` | Object | Named action constants |
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
Part of [Rokkit](https://github.com/jerrythomas/rokkit) — a Svelte 5 component library and design system.
|
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.134",
|
|
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",
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
12
|
"prepublishOnly": "cp ../../LICENSE . && tsc --project tsconfig.build.json",
|
|
13
|
+
"postpublish": "rm -f LICENSE",
|
|
13
14
|
"clean": "rm -rf dist",
|
|
14
15
|
"build": "bun clean && bun prepublishOnly"
|
|
15
16
|
},
|
package/src/hover-lift.svelte.js
CHANGED
|
@@ -20,8 +20,7 @@ export function hoverLift(node, options = {}) {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
const reducedMotion =
|
|
23
|
-
typeof window !== 'undefined' &&
|
|
24
|
-
window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
|
23
|
+
typeof window !== 'undefined' && window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
|
25
24
|
|
|
26
25
|
if (reducedMotion) return
|
|
27
26
|
|
package/src/magnetic.svelte.js
CHANGED
|
@@ -18,8 +18,7 @@ export function magnetic(node, options = {}) {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
const reducedMotion =
|
|
21
|
-
typeof window !== 'undefined' &&
|
|
22
|
-
window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
|
21
|
+
typeof window !== 'undefined' && window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
|
23
22
|
|
|
24
23
|
if (reducedMotion) return
|
|
25
24
|
|
package/src/navigator.js
CHANGED
|
@@ -153,9 +153,7 @@ export class Navigator {
|
|
|
153
153
|
// Focused the container itself (user tabbed in, no roving tabindex item yet)
|
|
154
154
|
// Redirect focus to the currently focused item, or first item if none
|
|
155
155
|
const targetKey = this.#wrapper.focusedKey
|
|
156
|
-
const selector = targetKey
|
|
157
|
-
? `[data-path="${targetKey}"]`
|
|
158
|
-
: '[data-path]:not([disabled])'
|
|
156
|
+
const selector = targetKey ? `[data-path="${targetKey}"]` : '[data-path]:not([disabled])'
|
|
159
157
|
const el = /** @type {HTMLElement|null} */ (this.#root.querySelector(selector))
|
|
160
158
|
if (el) {
|
|
161
159
|
el.focus()
|
package/src/navigator.svelte.js
CHANGED
|
@@ -162,8 +162,7 @@ export function navigator(node, options) {
|
|
|
162
162
|
resetTypeahead()
|
|
163
163
|
emitAction(node, wrapper, action, true)
|
|
164
164
|
// If expand/collapse moved focus, also emit move so components update DOM focus
|
|
165
|
-
const focusMoved =
|
|
166
|
-
['expand', 'collapse'].includes(action) && wrapper.focusedKey !== prevKey
|
|
165
|
+
const focusMoved = ['expand', 'collapse'].includes(action) && wrapper.focusedKey !== prevKey
|
|
167
166
|
if (focusMoved) {
|
|
168
167
|
node.dispatchEvent(
|
|
169
168
|
new CustomEvent('action', {
|
package/src/reveal.svelte.js
CHANGED
|
@@ -31,8 +31,7 @@ export function reveal(node, options = {}) {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
const reducedMotion =
|
|
34
|
-
typeof window !== 'undefined' &&
|
|
35
|
-
window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
|
34
|
+
typeof window !== 'undefined' && window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
|
36
35
|
|
|
37
36
|
const isStagger = opts.stagger > 0
|
|
38
37
|
|
package/src/ripple.svelte.js
CHANGED
|
@@ -20,8 +20,7 @@ export function ripple(node, options = {}) {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
const reducedMotion =
|
|
23
|
-
typeof window !== 'undefined' &&
|
|
24
|
-
window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
|
23
|
+
typeof window !== 'undefined' && window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
|
25
24
|
|
|
26
25
|
if (reducedMotion) return
|
|
27
26
|
|
package/src/trigger.js
CHANGED
package/src/utils.js
CHANGED
|
@@ -26,7 +26,6 @@ export function getClosestAncestorWithAttribute(element, attribute) {
|
|
|
26
26
|
* @returns {string|null} - The event name or null if no match is found.
|
|
27
27
|
*/
|
|
28
28
|
export const getEventForKey = (keyMapping, key) => {
|
|
29
|
-
|
|
30
29
|
const matchEvent = ([_eventName, keys]) =>
|
|
31
30
|
(Array.isArray(keys) && keys.includes(key)) || (keys instanceof RegExp && keys.test(key))
|
|
32
31
|
|