secret-sequence-react 1.0.0 → 1.0.1
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 +249 -249
- package/dist/index.js +31 -14
- package/dist/index.mjs +32 -15
- package/package.json +19 -9
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Diego Alonso
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,249 +1,249 @@
|
|
|
1
|
-
<div align="center">
|
|
2
|
-
|
|
3
|
-
# Secret Sequence React
|
|
4
|
-
|
|
5
|
-
[](./README.md)
|
|
6
|
-
[](./README_ES.md)
|
|
7
|
-
|
|
8
|
-

|
|
9
|
-

|
|
10
|
-

|
|
11
|
-
[](https://github.com/sponsors/Diego0Alonso)
|
|
12
|
-
|
|
13
|
-

|
|
15
|
-
|
|
16
|
-
> React hook & component for detecting directional sequences, key combos, and touch gestures — powered by [secret-sequence-core](../core/README.md).
|
|
17
|
-
|
|
18
|
-
This package provides **React bindings** for the [Secret Sequence](../../README.md) monorepo.
|
|
19
|
-
It wraps the core engine in a hook and a declarative component with full lifecycle management.
|
|
20
|
-
|
|
21
|
-
</div>
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
## Installation
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
npm install secret-sequence-react secret-sequence-core
|
|
29
|
-
````
|
|
30
|
-
|
|
31
|
-
> `secret-sequence-core` is a peer dependency.
|
|
32
|
-
|
|
33
|
-
---
|
|
34
|
-
|
|
35
|
-
## Quick Start
|
|
36
|
-
|
|
37
|
-
### Hook — `useSecretSequence`
|
|
38
|
-
|
|
39
|
-
```tsx
|
|
40
|
-
import { useSecretSequence } from "secret-sequence-react"
|
|
41
|
-
|
|
42
|
-
function App() {
|
|
43
|
-
const { progress } = useSecretSequence({
|
|
44
|
-
sequences: [
|
|
45
|
-
{
|
|
46
|
-
id: "konami",
|
|
47
|
-
sequence: ["up", "up", "down", "down", "left", "right", "left", "right"],
|
|
48
|
-
onSuccess: () => alert("🎉 Konami Code activated!"),
|
|
49
|
-
},
|
|
50
|
-
],
|
|
51
|
-
enableTouch: true,
|
|
52
|
-
touchOptions: { minDistance: 50, maxTime: 400 },
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
return <pre>{JSON.stringify(progress, null, 2)}</pre>
|
|
56
|
-
}
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
---
|
|
60
|
-
|
|
61
|
-
### Component — `<SecretSequence />`
|
|
62
|
-
|
|
63
|
-
#### Invisible Mode (effect only)
|
|
64
|
-
|
|
65
|
-
```tsx
|
|
66
|
-
import { SecretSequence } from "secret-sequence-react"
|
|
67
|
-
|
|
68
|
-
function App() {
|
|
69
|
-
return (
|
|
70
|
-
<SecretSequence
|
|
71
|
-
sequences={[
|
|
72
|
-
{
|
|
73
|
-
id: "konami",
|
|
74
|
-
sequence: ["up", "up", "down", "down", "left", "right", "left", "right"],
|
|
75
|
-
onSuccess: () => console.log("🎉"),
|
|
76
|
-
},
|
|
77
|
-
]}
|
|
78
|
-
enableTouch={true}
|
|
79
|
-
touchOptions={{ minDistance: 40 }}
|
|
80
|
-
/>
|
|
81
|
-
)
|
|
82
|
-
}
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
#### With render prop
|
|
86
|
-
|
|
87
|
-
```tsx
|
|
88
|
-
<SecretSequence
|
|
89
|
-
sequences={[{ id: "code", sequence: ["up", "down"], onSuccess: fn }]}
|
|
90
|
-
>
|
|
91
|
-
{({ progress, reset }) => (
|
|
92
|
-
<div>
|
|
93
|
-
<p>Progress: {JSON.stringify(progress)}</p>
|
|
94
|
-
<button onClick={reset}>Reset</button>
|
|
95
|
-
</div>
|
|
96
|
-
)}
|
|
97
|
-
</SecretSequence>
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
---
|
|
101
|
-
|
|
102
|
-
### Stratagem-Style Input
|
|
103
|
-
|
|
104
|
-
```tsx
|
|
105
|
-
const { progress } = useSecretSequence({
|
|
106
|
-
sequences: [
|
|
107
|
-
{
|
|
108
|
-
id: "orbitalStrike",
|
|
109
|
-
sequence: ["right", "right", "up"],
|
|
110
|
-
onSuccess: () => deployStrike(),
|
|
111
|
-
},
|
|
112
|
-
],
|
|
113
|
-
timeout: 2000,
|
|
114
|
-
})
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
---
|
|
118
|
-
|
|
119
|
-
### Key Combo Shortcut
|
|
120
|
-
|
|
121
|
-
```tsx
|
|
122
|
-
const { progress } = useSecretSequence({
|
|
123
|
-
sequences: [
|
|
124
|
-
{
|
|
125
|
-
id: "shortcut",
|
|
126
|
-
sequence: [{ key: "k", ctrl: true }],
|
|
127
|
-
onSuccess: () => console.log("Shortcut triggered"),
|
|
128
|
-
},
|
|
129
|
-
],
|
|
130
|
-
})
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
---
|
|
134
|
-
|
|
135
|
-
### Touch Gesture Support
|
|
136
|
-
|
|
137
|
-
Swipe gestures on touch devices are automatically mapped to directional steps.
|
|
138
|
-
|
|
139
|
-
```tsx
|
|
140
|
-
const { progress } = useSecretSequence({
|
|
141
|
-
sequences: [
|
|
142
|
-
{
|
|
143
|
-
id: "swipe-pattern",
|
|
144
|
-
sequence: ["up", "down", "left", "right"],
|
|
145
|
-
onSuccess: () => alert("Swipe pattern detected!"),
|
|
146
|
-
},
|
|
147
|
-
],
|
|
148
|
-
enableTouch: true,
|
|
149
|
-
touchOptions: {
|
|
150
|
-
minDistance: 30,
|
|
151
|
-
maxTime: 300,
|
|
152
|
-
threshold: 1.5,
|
|
153
|
-
},
|
|
154
|
-
})
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
---
|
|
158
|
-
|
|
159
|
-
## API
|
|
160
|
-
|
|
161
|
-
### `useSecretSequence(options)`
|
|
162
|
-
|
|
163
|
-
React hook that manages the engine lifecycle automatically.
|
|
164
|
-
|
|
165
|
-
**Returns:** `{ progress, reset }`
|
|
166
|
-
|
|
167
|
-
| Return | Type | Description |
|
|
168
|
-
| ---------- | -------------------------- | ----------------------------- |
|
|
169
|
-
| `progress` | `Record<string, number>` | Current progress per sequence |
|
|
170
|
-
| `reset` | `() => void` | Reset all sequence progress |
|
|
171
|
-
|
|
172
|
-
---
|
|
173
|
-
|
|
174
|
-
### `<SecretSequence />` Component
|
|
175
|
-
|
|
176
|
-
Declarative JSX wrapper over the `useSecretSequence` hook.
|
|
177
|
-
|
|
178
|
-
| Prop | Type | Description |
|
|
179
|
-
| ------------------ | ----------------------------------------------------------------------- | ---------------------------------------- |
|
|
180
|
-
| `onProgressChange` | `(progress: Record<string, number>) => void` | Callback fired on progress changes |
|
|
181
|
-
| `children` | `(state: { progress: Record<string, number>; reset: () => void }) => …` | Optional render prop for progress/reset |
|
|
182
|
-
| *...hookOptions* | `UseSecretSequenceOptions` | All hook options (see below) |
|
|
183
|
-
|
|
184
|
-
---
|
|
185
|
-
|
|
186
|
-
### Options (shared by hook & component)
|
|
187
|
-
|
|
188
|
-
| Option | Type | Default | Description |
|
|
189
|
-
| -------------- | ------------------------ | ------- | ------------------------------------------------------ |
|
|
190
|
-
| `sequences` | `SecretSequenceConfig[]` | — | Array of sequences to detect simultaneously |
|
|
191
|
-
| `timeout` | `number` | `2000` | Milliseconds of inactivity before resetting progress |
|
|
192
|
-
| `enabled` | `boolean` | `true` | Globally enable or disable detection |
|
|
193
|
-
| `enableTouch` | `boolean` | `true` | Enable swipe gesture detection |
|
|
194
|
-
| `ignoreInputs` | `boolean` | `true` | Ignore key events when focus is on input-like elements |
|
|
195
|
-
| `touchOptions` | `TouchConfig` | — | Advanced touch configuration |
|
|
196
|
-
|
|
197
|
-
---
|
|
198
|
-
|
|
199
|
-
## Configuration Types
|
|
200
|
-
|
|
201
|
-
### `SecretSequenceConfig`
|
|
202
|
-
|
|
203
|
-
| Property | Type | Description |
|
|
204
|
-
| ----------- | --------------------- | ------------------------------------------- |
|
|
205
|
-
| `id` | `string` *(optional)* | Unique identifier (defaults to array index) |
|
|
206
|
-
| `sequence` | `SequenceStep[]` | Ordered steps to detect |
|
|
207
|
-
| `onSuccess` | `() => void` | Fired when the sequence completes |
|
|
208
|
-
|
|
209
|
-
---
|
|
210
|
-
|
|
211
|
-
### Core Types
|
|
212
|
-
|
|
213
|
-
```ts
|
|
214
|
-
type Direction = "up" | "down" | "left" | "right"
|
|
215
|
-
|
|
216
|
-
type KeyCombo = {
|
|
217
|
-
key: string
|
|
218
|
-
ctrl?: boolean
|
|
219
|
-
shift?: boolean
|
|
220
|
-
alt?: boolean
|
|
221
|
-
meta?: boolean
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
type SequenceStep = Direction | KeyCombo
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
---
|
|
228
|
-
|
|
229
|
-
### `TouchConfig`
|
|
230
|
-
|
|
231
|
-
| Property | Type | Default | Description |
|
|
232
|
-
| ------------- | -------- | ------- | ---------------------------------------------- |
|
|
233
|
-
| `minDistance` | `number` | `30` | Minimum swipe distance (px) |
|
|
234
|
-
| `maxTime` | `number` | `300` | Maximum swipe duration (ms) |
|
|
235
|
-
| `threshold` | `number` | `1.5` | Axis dominance ratio to reject diagonal swipes |
|
|
236
|
-
|
|
237
|
-
---
|
|
238
|
-
|
|
239
|
-
## SSR Compatibility
|
|
240
|
-
|
|
241
|
-
This package is safe to use in SSR environments such as Next.js or Remix.
|
|
242
|
-
|
|
243
|
-
The underlying engine guards against accessing `window` during server rendering and only attaches event listeners in the browser.
|
|
244
|
-
|
|
245
|
-
---
|
|
246
|
-
|
|
247
|
-
## License
|
|
248
|
-
|
|
249
|
-
MIT © Diego Alonso
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# Secret Sequence React
|
|
4
|
+
|
|
5
|
+
[](./README.md)
|
|
6
|
+
[](./README_ES.md)
|
|
7
|
+
|
|
8
|
+

|
|
9
|
+

|
|
10
|
+

|
|
11
|
+
[](https://github.com/sponsors/Diego0Alonso)
|
|
12
|
+
|
|
13
|
+

|
|
14
|
+

|
|
15
|
+
|
|
16
|
+
> React hook & component for detecting directional sequences, key combos, and touch gestures — powered by [secret-sequence-core](../core/README.md).
|
|
17
|
+
|
|
18
|
+
This package provides **React bindings** for the [Secret Sequence](../../README.md) monorepo.
|
|
19
|
+
It wraps the core engine in a hook and a declarative component with full lifecycle management.
|
|
20
|
+
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install secret-sequence-react secret-sequence-core
|
|
29
|
+
````
|
|
30
|
+
|
|
31
|
+
> `secret-sequence-core` is a peer dependency.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
### Hook — `useSecretSequence`
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
import { useSecretSequence } from "secret-sequence-react"
|
|
41
|
+
|
|
42
|
+
function App() {
|
|
43
|
+
const { progress } = useSecretSequence({
|
|
44
|
+
sequences: [
|
|
45
|
+
{
|
|
46
|
+
id: "konami",
|
|
47
|
+
sequence: ["up", "up", "down", "down", "left", "right", "left", "right"],
|
|
48
|
+
onSuccess: () => alert("🎉 Konami Code activated!"),
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
enableTouch: true,
|
|
52
|
+
touchOptions: { minDistance: 50, maxTime: 400 },
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
return <pre>{JSON.stringify(progress, null, 2)}</pre>
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
### Component — `<SecretSequence />`
|
|
62
|
+
|
|
63
|
+
#### Invisible Mode (effect only)
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
import { SecretSequence } from "secret-sequence-react"
|
|
67
|
+
|
|
68
|
+
function App() {
|
|
69
|
+
return (
|
|
70
|
+
<SecretSequence
|
|
71
|
+
sequences={[
|
|
72
|
+
{
|
|
73
|
+
id: "konami",
|
|
74
|
+
sequence: ["up", "up", "down", "down", "left", "right", "left", "right"],
|
|
75
|
+
onSuccess: () => console.log("🎉"),
|
|
76
|
+
},
|
|
77
|
+
]}
|
|
78
|
+
enableTouch={true}
|
|
79
|
+
touchOptions={{ minDistance: 40 }}
|
|
80
|
+
/>
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
#### With render prop
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
<SecretSequence
|
|
89
|
+
sequences={[{ id: "code", sequence: ["up", "down"], onSuccess: fn }]}
|
|
90
|
+
>
|
|
91
|
+
{({ progress, reset }) => (
|
|
92
|
+
<div>
|
|
93
|
+
<p>Progress: {JSON.stringify(progress)}</p>
|
|
94
|
+
<button onClick={reset}>Reset</button>
|
|
95
|
+
</div>
|
|
96
|
+
)}
|
|
97
|
+
</SecretSequence>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
### Stratagem-Style Input
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
const { progress } = useSecretSequence({
|
|
106
|
+
sequences: [
|
|
107
|
+
{
|
|
108
|
+
id: "orbitalStrike",
|
|
109
|
+
sequence: ["right", "right", "up"],
|
|
110
|
+
onSuccess: () => deployStrike(),
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
timeout: 2000,
|
|
114
|
+
})
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
### Key Combo Shortcut
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
const { progress } = useSecretSequence({
|
|
123
|
+
sequences: [
|
|
124
|
+
{
|
|
125
|
+
id: "shortcut",
|
|
126
|
+
sequence: [{ key: "k", ctrl: true }],
|
|
127
|
+
onSuccess: () => console.log("Shortcut triggered"),
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
})
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
### Touch Gesture Support
|
|
136
|
+
|
|
137
|
+
Swipe gestures on touch devices are automatically mapped to directional steps.
|
|
138
|
+
|
|
139
|
+
```tsx
|
|
140
|
+
const { progress } = useSecretSequence({
|
|
141
|
+
sequences: [
|
|
142
|
+
{
|
|
143
|
+
id: "swipe-pattern",
|
|
144
|
+
sequence: ["up", "down", "left", "right"],
|
|
145
|
+
onSuccess: () => alert("Swipe pattern detected!"),
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
enableTouch: true,
|
|
149
|
+
touchOptions: {
|
|
150
|
+
minDistance: 30,
|
|
151
|
+
maxTime: 300,
|
|
152
|
+
threshold: 1.5,
|
|
153
|
+
},
|
|
154
|
+
})
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## API
|
|
160
|
+
|
|
161
|
+
### `useSecretSequence(options)`
|
|
162
|
+
|
|
163
|
+
React hook that manages the engine lifecycle automatically.
|
|
164
|
+
|
|
165
|
+
**Returns:** `{ progress, reset }`
|
|
166
|
+
|
|
167
|
+
| Return | Type | Description |
|
|
168
|
+
| ---------- | -------------------------- | ----------------------------- |
|
|
169
|
+
| `progress` | `Record<string, number>` | Current progress per sequence |
|
|
170
|
+
| `reset` | `() => void` | Reset all sequence progress |
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
### `<SecretSequence />` Component
|
|
175
|
+
|
|
176
|
+
Declarative JSX wrapper over the `useSecretSequence` hook.
|
|
177
|
+
|
|
178
|
+
| Prop | Type | Description |
|
|
179
|
+
| ------------------ | ----------------------------------------------------------------------- | ---------------------------------------- |
|
|
180
|
+
| `onProgressChange` | `(progress: Record<string, number>) => void` | Callback fired on progress changes |
|
|
181
|
+
| `children` | `(state: { progress: Record<string, number>; reset: () => void }) => …` | Optional render prop for progress/reset |
|
|
182
|
+
| *...hookOptions* | `UseSecretSequenceOptions` | All hook options (see below) |
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
### Options (shared by hook & component)
|
|
187
|
+
|
|
188
|
+
| Option | Type | Default | Description |
|
|
189
|
+
| -------------- | ------------------------ | ------- | ------------------------------------------------------ |
|
|
190
|
+
| `sequences` | `SecretSequenceConfig[]` | — | Array of sequences to detect simultaneously |
|
|
191
|
+
| `timeout` | `number` | `2000` | Milliseconds of inactivity before resetting progress |
|
|
192
|
+
| `enabled` | `boolean` | `true` | Globally enable or disable detection |
|
|
193
|
+
| `enableTouch` | `boolean` | `true` | Enable swipe gesture detection |
|
|
194
|
+
| `ignoreInputs` | `boolean` | `true` | Ignore key events when focus is on input-like elements |
|
|
195
|
+
| `touchOptions` | `TouchConfig` | — | Advanced touch configuration |
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Configuration Types
|
|
200
|
+
|
|
201
|
+
### `SecretSequenceConfig`
|
|
202
|
+
|
|
203
|
+
| Property | Type | Description |
|
|
204
|
+
| ----------- | --------------------- | ------------------------------------------- |
|
|
205
|
+
| `id` | `string` *(optional)* | Unique identifier (defaults to array index) |
|
|
206
|
+
| `sequence` | `SequenceStep[]` | Ordered steps to detect |
|
|
207
|
+
| `onSuccess` | `() => void` | Fired when the sequence completes |
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
### Core Types
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
type Direction = "up" | "down" | "left" | "right"
|
|
215
|
+
|
|
216
|
+
type KeyCombo = {
|
|
217
|
+
key: string
|
|
218
|
+
ctrl?: boolean
|
|
219
|
+
shift?: boolean
|
|
220
|
+
alt?: boolean
|
|
221
|
+
meta?: boolean
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
type SequenceStep = Direction | KeyCombo
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
### `TouchConfig`
|
|
230
|
+
|
|
231
|
+
| Property | Type | Default | Description |
|
|
232
|
+
| ------------- | -------- | ------- | ---------------------------------------------- |
|
|
233
|
+
| `minDistance` | `number` | `30` | Minimum swipe distance (px) |
|
|
234
|
+
| `maxTime` | `number` | `300` | Maximum swipe duration (ms) |
|
|
235
|
+
| `threshold` | `number` | `1.5` | Axis dominance ratio to reject diagonal swipes |
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## SSR Compatibility
|
|
240
|
+
|
|
241
|
+
This package is safe to use in SSR environments such as Next.js or Remix.
|
|
242
|
+
|
|
243
|
+
The underlying engine guards against accessing `window` during server rendering and only attaches event listeners in the browser.
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## License
|
|
248
|
+
|
|
249
|
+
MIT © Diego Alonso
|
package/dist/index.js
CHANGED
|
@@ -28,6 +28,14 @@ module.exports = __toCommonJS(index_exports);
|
|
|
28
28
|
// src/useSecretSequence.ts
|
|
29
29
|
var import_react = require("react");
|
|
30
30
|
var import_secret_sequence_core = require("secret-sequence-core");
|
|
31
|
+
function isSameProgress(a, b) {
|
|
32
|
+
const aKeys = Object.keys(a);
|
|
33
|
+
if (aKeys.length !== Object.keys(b).length) return false;
|
|
34
|
+
for (const key of aKeys) {
|
|
35
|
+
if (a[key] !== b[key]) return false;
|
|
36
|
+
}
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
31
39
|
function useSecretSequence(options) {
|
|
32
40
|
const {
|
|
33
41
|
sequences,
|
|
@@ -47,19 +55,32 @@ function useSecretSequence(options) {
|
|
|
47
55
|
sequences.map(({ onSuccess, ...rest }) => rest)
|
|
48
56
|
);
|
|
49
57
|
const touchOptionsKey = JSON.stringify(touchOptions);
|
|
58
|
+
const stableSequences = (0, import_react.useMemo)(
|
|
59
|
+
() => sequencesRef.current.map((seq, i) => ({
|
|
60
|
+
...seq,
|
|
61
|
+
onSuccess: () => sequencesRef.current[i]?.onSuccess()
|
|
62
|
+
})),
|
|
63
|
+
// Se recrea solo cuando cambian teclas/id/orden/cantidad, no el callback.
|
|
64
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
65
|
+
[sequencesKey]
|
|
66
|
+
);
|
|
67
|
+
const stableSequencesRef = (0, import_react.useRef)(stableSequences);
|
|
68
|
+
stableSequencesRef.current = stableSequences;
|
|
69
|
+
const syncProgress = (0, import_react.useCallback)(() => {
|
|
70
|
+
const engine = engineRef.current;
|
|
71
|
+
if (!engine) return;
|
|
72
|
+
const next = engine.getProgressMap();
|
|
73
|
+
setProgress((prev) => isSameProgress(prev, next) ? prev : next);
|
|
74
|
+
}, []);
|
|
50
75
|
(0, import_react.useEffect)(() => {
|
|
51
76
|
const engineOptions = {
|
|
52
|
-
sequences:
|
|
77
|
+
sequences: stableSequencesRef.current,
|
|
53
78
|
timeout,
|
|
54
79
|
enabled,
|
|
55
80
|
enableTouch,
|
|
56
81
|
ignoreInputs,
|
|
57
82
|
touchOptions: touchOptionsRef.current,
|
|
58
|
-
onProgress:
|
|
59
|
-
if (engineRef.current) {
|
|
60
|
-
setProgress(engineRef.current.getProgressMap());
|
|
61
|
-
}
|
|
62
|
-
}
|
|
83
|
+
onProgress: syncProgress
|
|
63
84
|
};
|
|
64
85
|
const engine = new import_secret_sequence_core.SecretSequenceEngine(engineOptions);
|
|
65
86
|
engineRef.current = engine;
|
|
@@ -69,20 +90,16 @@ function useSecretSequence(options) {
|
|
|
69
90
|
engine.destroy();
|
|
70
91
|
engineRef.current = null;
|
|
71
92
|
};
|
|
72
|
-
}, [timeout, enabled, enableTouch, ignoreInputs]);
|
|
93
|
+
}, [timeout, enabled, enableTouch, ignoreInputs, syncProgress]);
|
|
73
94
|
(0, import_react.useEffect)(() => {
|
|
74
95
|
if (!engineRef.current) return;
|
|
75
96
|
engineRef.current.setOptions({
|
|
76
|
-
sequences:
|
|
97
|
+
sequences: stableSequencesRef.current,
|
|
77
98
|
touchOptions: touchOptionsRef.current,
|
|
78
|
-
onProgress:
|
|
79
|
-
if (engineRef.current) {
|
|
80
|
-
setProgress(engineRef.current.getProgressMap());
|
|
81
|
-
}
|
|
82
|
-
}
|
|
99
|
+
onProgress: syncProgress
|
|
83
100
|
});
|
|
84
101
|
setProgress(engineRef.current.getProgressMap());
|
|
85
|
-
}, [sequencesKey, touchOptionsKey]);
|
|
102
|
+
}, [sequencesKey, touchOptionsKey, syncProgress]);
|
|
86
103
|
const reset = (0, import_react.useCallback)(() => {
|
|
87
104
|
if (engineRef.current) {
|
|
88
105
|
engineRef.current.reset();
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
// src/useSecretSequence.ts
|
|
2
|
-
import { useEffect, useRef, useState, useCallback } from "react";
|
|
2
|
+
import { useEffect, useMemo, useRef, useState, useCallback } from "react";
|
|
3
3
|
import {
|
|
4
4
|
SecretSequenceEngine
|
|
5
5
|
} from "secret-sequence-core";
|
|
6
|
+
function isSameProgress(a, b) {
|
|
7
|
+
const aKeys = Object.keys(a);
|
|
8
|
+
if (aKeys.length !== Object.keys(b).length) return false;
|
|
9
|
+
for (const key of aKeys) {
|
|
10
|
+
if (a[key] !== b[key]) return false;
|
|
11
|
+
}
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
6
14
|
function useSecretSequence(options) {
|
|
7
15
|
const {
|
|
8
16
|
sequences,
|
|
@@ -22,19 +30,32 @@ function useSecretSequence(options) {
|
|
|
22
30
|
sequences.map(({ onSuccess, ...rest }) => rest)
|
|
23
31
|
);
|
|
24
32
|
const touchOptionsKey = JSON.stringify(touchOptions);
|
|
33
|
+
const stableSequences = useMemo(
|
|
34
|
+
() => sequencesRef.current.map((seq, i) => ({
|
|
35
|
+
...seq,
|
|
36
|
+
onSuccess: () => sequencesRef.current[i]?.onSuccess()
|
|
37
|
+
})),
|
|
38
|
+
// Se recrea solo cuando cambian teclas/id/orden/cantidad, no el callback.
|
|
39
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
40
|
+
[sequencesKey]
|
|
41
|
+
);
|
|
42
|
+
const stableSequencesRef = useRef(stableSequences);
|
|
43
|
+
stableSequencesRef.current = stableSequences;
|
|
44
|
+
const syncProgress = useCallback(() => {
|
|
45
|
+
const engine = engineRef.current;
|
|
46
|
+
if (!engine) return;
|
|
47
|
+
const next = engine.getProgressMap();
|
|
48
|
+
setProgress((prev) => isSameProgress(prev, next) ? prev : next);
|
|
49
|
+
}, []);
|
|
25
50
|
useEffect(() => {
|
|
26
51
|
const engineOptions = {
|
|
27
|
-
sequences:
|
|
52
|
+
sequences: stableSequencesRef.current,
|
|
28
53
|
timeout,
|
|
29
54
|
enabled,
|
|
30
55
|
enableTouch,
|
|
31
56
|
ignoreInputs,
|
|
32
57
|
touchOptions: touchOptionsRef.current,
|
|
33
|
-
onProgress:
|
|
34
|
-
if (engineRef.current) {
|
|
35
|
-
setProgress(engineRef.current.getProgressMap());
|
|
36
|
-
}
|
|
37
|
-
}
|
|
58
|
+
onProgress: syncProgress
|
|
38
59
|
};
|
|
39
60
|
const engine = new SecretSequenceEngine(engineOptions);
|
|
40
61
|
engineRef.current = engine;
|
|
@@ -44,20 +65,16 @@ function useSecretSequence(options) {
|
|
|
44
65
|
engine.destroy();
|
|
45
66
|
engineRef.current = null;
|
|
46
67
|
};
|
|
47
|
-
}, [timeout, enabled, enableTouch, ignoreInputs]);
|
|
68
|
+
}, [timeout, enabled, enableTouch, ignoreInputs, syncProgress]);
|
|
48
69
|
useEffect(() => {
|
|
49
70
|
if (!engineRef.current) return;
|
|
50
71
|
engineRef.current.setOptions({
|
|
51
|
-
sequences:
|
|
72
|
+
sequences: stableSequencesRef.current,
|
|
52
73
|
touchOptions: touchOptionsRef.current,
|
|
53
|
-
onProgress:
|
|
54
|
-
if (engineRef.current) {
|
|
55
|
-
setProgress(engineRef.current.getProgressMap());
|
|
56
|
-
}
|
|
57
|
-
}
|
|
74
|
+
onProgress: syncProgress
|
|
58
75
|
});
|
|
59
76
|
setProgress(engineRef.current.getProgressMap());
|
|
60
|
-
}, [sequencesKey, touchOptionsKey]);
|
|
77
|
+
}, [sequencesKey, touchOptionsKey, syncProgress]);
|
|
61
78
|
const reset = useCallback(() => {
|
|
62
79
|
if (engineRef.current) {
|
|
63
80
|
engineRef.current.reset();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "secret-sequence-react",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "React hook & component wrapper for secret-sequence-core — detect directional sequences, key combos, and touch gestures",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -22,9 +22,14 @@
|
|
|
22
22
|
"license": "MIT",
|
|
23
23
|
"repository": {
|
|
24
24
|
"type": "git",
|
|
25
|
-
"url": "git+https://github.com/
|
|
25
|
+
"url": "git+https://github.com/Diego0Alonso/secret-sequence.git",
|
|
26
26
|
"directory": "packages/react"
|
|
27
27
|
},
|
|
28
|
+
"homepage": "https://github.com/Diego0Alonso/secret-sequence/tree/main/packages/react#readme",
|
|
29
|
+
"bugs": "https://github.com/Diego0Alonso/secret-sequence/issues",
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=18"
|
|
32
|
+
},
|
|
28
33
|
"main": "dist/index.js",
|
|
29
34
|
"module": "dist/index.mjs",
|
|
30
35
|
"types": "dist/index.d.ts",
|
|
@@ -39,19 +44,24 @@
|
|
|
39
44
|
"files": [
|
|
40
45
|
"dist"
|
|
41
46
|
],
|
|
42
|
-
"scripts": {
|
|
43
|
-
"build": "tsup",
|
|
44
|
-
"prepublishOnly": "npm run build"
|
|
45
|
-
},
|
|
46
47
|
"peerDependencies": {
|
|
47
|
-
"react": ">=
|
|
48
|
+
"react": ">=18.0.0",
|
|
48
49
|
"secret-sequence-core": "^2.0.0"
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|
|
52
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
53
|
+
"@testing-library/react": "^16.3.2",
|
|
51
54
|
"@types/react": "^19.0.0",
|
|
55
|
+
"jsdom": "^28.1.0",
|
|
52
56
|
"react": "^19.0.0",
|
|
53
|
-
"secret-sequence-core": "^2.0.0",
|
|
54
57
|
"tsup": "^8.0.0",
|
|
55
|
-
"typescript": "^5.0.0"
|
|
58
|
+
"typescript": "^5.0.0",
|
|
59
|
+
"vitest": "^4.0.18",
|
|
60
|
+
"secret-sequence-core": "^2.1.0"
|
|
61
|
+
},
|
|
62
|
+
"scripts": {
|
|
63
|
+
"build": "tsup",
|
|
64
|
+
"test": "vitest run",
|
|
65
|
+
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
56
66
|
}
|
|
57
67
|
}
|