secret-sequence-react 1.0.0
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 +249 -0
- package/dist/index.d.mts +137 -0
- package/dist/index.d.ts +137 -0
- package/dist/index.js +118 -0
- package/dist/index.mjs +92 -0
- package/package.json +57 -0
package/README.md
ADDED
|
@@ -0,0 +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
|
+

|
|
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.d.mts
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { SecretSequenceConfig, TouchConfig } from 'secret-sequence-core';
|
|
2
|
+
export { Direction, KeyCombo, SecretSequenceConfig, SecretSequenceEngineOptions, SequenceStep, SwipeDirection, TouchConfig, TouchPoint } from 'secret-sequence-core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Opciones para el hook useSecretSequence.
|
|
6
|
+
* Refleja las opciones del engine del core.
|
|
7
|
+
*/
|
|
8
|
+
interface UseSecretSequenceOptions {
|
|
9
|
+
/** Lista de secuencias a detectar */
|
|
10
|
+
sequences: SecretSequenceConfig[];
|
|
11
|
+
/** Tiempo máximo entre pasos antes de resetear (ms). Default: 2000 */
|
|
12
|
+
timeout?: number;
|
|
13
|
+
/** Habilitar/deshabilitar la detección. Default: true */
|
|
14
|
+
enabled?: boolean;
|
|
15
|
+
/** Habilitar detección de gestos táctiles (swipes). Default: true */
|
|
16
|
+
enableTouch?: boolean;
|
|
17
|
+
/** Ignorar eventos cuando el foco está en inputs/textareas. Default: true */
|
|
18
|
+
ignoreInputs?: boolean;
|
|
19
|
+
/** Configuración de sensibilidad para gestos táctiles */
|
|
20
|
+
touchOptions?: TouchConfig;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Valor retornado por el hook useSecretSequence.
|
|
24
|
+
*/
|
|
25
|
+
interface UseSecretSequenceReturn {
|
|
26
|
+
/** Mapa de progreso actual: { [sequenceId]: stepsCompleted } */
|
|
27
|
+
progress: Record<string, number>;
|
|
28
|
+
/** Resetea el progreso de todas las secuencias */
|
|
29
|
+
reset: () => void;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Hook de React para detectar secuencias de teclado (Konami codes, combos de teclas)
|
|
33
|
+
* y gestos táctiles (swipes) usando el engine de secret-sequence-core.
|
|
34
|
+
*
|
|
35
|
+
* Gestiona automáticamente el ciclo de vida del engine:
|
|
36
|
+
* - Se inicia al montar el componente
|
|
37
|
+
* - Se actualiza reactivamente cuando cambian las opciones
|
|
38
|
+
* - Se destruye al desmontar el componente
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```tsx
|
|
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: () => console.log("🎉 Konami activado!"),
|
|
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
|
+
* @example Touch-only (sin teclado)
|
|
60
|
+
* ```tsx
|
|
61
|
+
* const { progress } = useSecretSequence({
|
|
62
|
+
* sequences: [
|
|
63
|
+
* {
|
|
64
|
+
* id: "swipe-pattern",
|
|
65
|
+
* sequence: ["up", "down", "left", "right"],
|
|
66
|
+
* onSuccess: () => alert("Patrón de swipe detectado!"),
|
|
67
|
+
* },
|
|
68
|
+
* ],
|
|
69
|
+
* enableTouch: true,
|
|
70
|
+
* touchOptions: {
|
|
71
|
+
* minDistance: 30,
|
|
72
|
+
* maxTime: 300,
|
|
73
|
+
* threshold: 1.5,
|
|
74
|
+
* },
|
|
75
|
+
* })
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
declare function useSecretSequence(options: UseSecretSequenceOptions): UseSecretSequenceReturn;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Props del componente SecretSequence.
|
|
82
|
+
* Idénticas a UseSecretSequenceOptions, con callbacks y render prop opcionales.
|
|
83
|
+
*/
|
|
84
|
+
interface SecretSequenceProps extends UseSecretSequenceOptions {
|
|
85
|
+
/** Callback invocado cuando cambia el progreso de alguna secuencia */
|
|
86
|
+
onProgressChange?: (progress: Record<string, number>) => void;
|
|
87
|
+
/** Render prop opcional para acceder al progreso y reset */
|
|
88
|
+
children?: (state: {
|
|
89
|
+
progress: Record<string, number>;
|
|
90
|
+
reset: () => void;
|
|
91
|
+
}) => React.ReactNode;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Componente declarativo para detectar secuencias secretas.
|
|
95
|
+
* Wrapper JSX sobre el hook `useSecretSequence`.
|
|
96
|
+
*
|
|
97
|
+
* Si no se pasa `children`, no renderiza nada.
|
|
98
|
+
* Si se pasa `children` como render prop, recibe `{ progress, reset }`.
|
|
99
|
+
*
|
|
100
|
+
* Soporta todas las funcionalidades del core:
|
|
101
|
+
* - Secuencias de teclado (direcciones y combos de teclas)
|
|
102
|
+
* - Gestos táctiles (swipes) con configuración personalizable
|
|
103
|
+
* - Timeout entre pasos
|
|
104
|
+
* - Ignorar inputs/textareas
|
|
105
|
+
*
|
|
106
|
+
* @example Modo invisible (solo efecto)
|
|
107
|
+
* ```tsx
|
|
108
|
+
* <SecretSequence
|
|
109
|
+
* sequences={[
|
|
110
|
+
* {
|
|
111
|
+
* id: "konami",
|
|
112
|
+
* sequence: ["up", "up", "down", "down", "left", "right", "left", "right"],
|
|
113
|
+
* onSuccess: () => console.log("🎉"),
|
|
114
|
+
* },
|
|
115
|
+
* ]}
|
|
116
|
+
* enableTouch={true}
|
|
117
|
+
* touchOptions={{ minDistance: 40 }}
|
|
118
|
+
* />
|
|
119
|
+
* ```
|
|
120
|
+
*
|
|
121
|
+
* @example Con render prop para mostrar progreso
|
|
122
|
+
* ```tsx
|
|
123
|
+
* <SecretSequence
|
|
124
|
+
* sequences={[{ id: "code", sequence: ["up", "down"], onSuccess: fn }]}
|
|
125
|
+
* >
|
|
126
|
+
* {({ progress, reset }) => (
|
|
127
|
+
* <div>
|
|
128
|
+
* <p>Progreso: {JSON.stringify(progress)}</p>
|
|
129
|
+
* <button onClick={reset}>Reset</button>
|
|
130
|
+
* </div>
|
|
131
|
+
* )}
|
|
132
|
+
* </SecretSequence>
|
|
133
|
+
* ```
|
|
134
|
+
*/
|
|
135
|
+
declare function SecretSequence(props: SecretSequenceProps): React.ReactNode;
|
|
136
|
+
|
|
137
|
+
export { SecretSequence, type SecretSequenceProps, type UseSecretSequenceOptions, type UseSecretSequenceReturn, useSecretSequence };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { SecretSequenceConfig, TouchConfig } from 'secret-sequence-core';
|
|
2
|
+
export { Direction, KeyCombo, SecretSequenceConfig, SecretSequenceEngineOptions, SequenceStep, SwipeDirection, TouchConfig, TouchPoint } from 'secret-sequence-core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Opciones para el hook useSecretSequence.
|
|
6
|
+
* Refleja las opciones del engine del core.
|
|
7
|
+
*/
|
|
8
|
+
interface UseSecretSequenceOptions {
|
|
9
|
+
/** Lista de secuencias a detectar */
|
|
10
|
+
sequences: SecretSequenceConfig[];
|
|
11
|
+
/** Tiempo máximo entre pasos antes de resetear (ms). Default: 2000 */
|
|
12
|
+
timeout?: number;
|
|
13
|
+
/** Habilitar/deshabilitar la detección. Default: true */
|
|
14
|
+
enabled?: boolean;
|
|
15
|
+
/** Habilitar detección de gestos táctiles (swipes). Default: true */
|
|
16
|
+
enableTouch?: boolean;
|
|
17
|
+
/** Ignorar eventos cuando el foco está en inputs/textareas. Default: true */
|
|
18
|
+
ignoreInputs?: boolean;
|
|
19
|
+
/** Configuración de sensibilidad para gestos táctiles */
|
|
20
|
+
touchOptions?: TouchConfig;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Valor retornado por el hook useSecretSequence.
|
|
24
|
+
*/
|
|
25
|
+
interface UseSecretSequenceReturn {
|
|
26
|
+
/** Mapa de progreso actual: { [sequenceId]: stepsCompleted } */
|
|
27
|
+
progress: Record<string, number>;
|
|
28
|
+
/** Resetea el progreso de todas las secuencias */
|
|
29
|
+
reset: () => void;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Hook de React para detectar secuencias de teclado (Konami codes, combos de teclas)
|
|
33
|
+
* y gestos táctiles (swipes) usando el engine de secret-sequence-core.
|
|
34
|
+
*
|
|
35
|
+
* Gestiona automáticamente el ciclo de vida del engine:
|
|
36
|
+
* - Se inicia al montar el componente
|
|
37
|
+
* - Se actualiza reactivamente cuando cambian las opciones
|
|
38
|
+
* - Se destruye al desmontar el componente
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```tsx
|
|
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: () => console.log("🎉 Konami activado!"),
|
|
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
|
+
* @example Touch-only (sin teclado)
|
|
60
|
+
* ```tsx
|
|
61
|
+
* const { progress } = useSecretSequence({
|
|
62
|
+
* sequences: [
|
|
63
|
+
* {
|
|
64
|
+
* id: "swipe-pattern",
|
|
65
|
+
* sequence: ["up", "down", "left", "right"],
|
|
66
|
+
* onSuccess: () => alert("Patrón de swipe detectado!"),
|
|
67
|
+
* },
|
|
68
|
+
* ],
|
|
69
|
+
* enableTouch: true,
|
|
70
|
+
* touchOptions: {
|
|
71
|
+
* minDistance: 30,
|
|
72
|
+
* maxTime: 300,
|
|
73
|
+
* threshold: 1.5,
|
|
74
|
+
* },
|
|
75
|
+
* })
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
declare function useSecretSequence(options: UseSecretSequenceOptions): UseSecretSequenceReturn;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Props del componente SecretSequence.
|
|
82
|
+
* Idénticas a UseSecretSequenceOptions, con callbacks y render prop opcionales.
|
|
83
|
+
*/
|
|
84
|
+
interface SecretSequenceProps extends UseSecretSequenceOptions {
|
|
85
|
+
/** Callback invocado cuando cambia el progreso de alguna secuencia */
|
|
86
|
+
onProgressChange?: (progress: Record<string, number>) => void;
|
|
87
|
+
/** Render prop opcional para acceder al progreso y reset */
|
|
88
|
+
children?: (state: {
|
|
89
|
+
progress: Record<string, number>;
|
|
90
|
+
reset: () => void;
|
|
91
|
+
}) => React.ReactNode;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Componente declarativo para detectar secuencias secretas.
|
|
95
|
+
* Wrapper JSX sobre el hook `useSecretSequence`.
|
|
96
|
+
*
|
|
97
|
+
* Si no se pasa `children`, no renderiza nada.
|
|
98
|
+
* Si se pasa `children` como render prop, recibe `{ progress, reset }`.
|
|
99
|
+
*
|
|
100
|
+
* Soporta todas las funcionalidades del core:
|
|
101
|
+
* - Secuencias de teclado (direcciones y combos de teclas)
|
|
102
|
+
* - Gestos táctiles (swipes) con configuración personalizable
|
|
103
|
+
* - Timeout entre pasos
|
|
104
|
+
* - Ignorar inputs/textareas
|
|
105
|
+
*
|
|
106
|
+
* @example Modo invisible (solo efecto)
|
|
107
|
+
* ```tsx
|
|
108
|
+
* <SecretSequence
|
|
109
|
+
* sequences={[
|
|
110
|
+
* {
|
|
111
|
+
* id: "konami",
|
|
112
|
+
* sequence: ["up", "up", "down", "down", "left", "right", "left", "right"],
|
|
113
|
+
* onSuccess: () => console.log("🎉"),
|
|
114
|
+
* },
|
|
115
|
+
* ]}
|
|
116
|
+
* enableTouch={true}
|
|
117
|
+
* touchOptions={{ minDistance: 40 }}
|
|
118
|
+
* />
|
|
119
|
+
* ```
|
|
120
|
+
*
|
|
121
|
+
* @example Con render prop para mostrar progreso
|
|
122
|
+
* ```tsx
|
|
123
|
+
* <SecretSequence
|
|
124
|
+
* sequences={[{ id: "code", sequence: ["up", "down"], onSuccess: fn }]}
|
|
125
|
+
* >
|
|
126
|
+
* {({ progress, reset }) => (
|
|
127
|
+
* <div>
|
|
128
|
+
* <p>Progreso: {JSON.stringify(progress)}</p>
|
|
129
|
+
* <button onClick={reset}>Reset</button>
|
|
130
|
+
* </div>
|
|
131
|
+
* )}
|
|
132
|
+
* </SecretSequence>
|
|
133
|
+
* ```
|
|
134
|
+
*/
|
|
135
|
+
declare function SecretSequence(props: SecretSequenceProps): React.ReactNode;
|
|
136
|
+
|
|
137
|
+
export { SecretSequence, type SecretSequenceProps, type UseSecretSequenceOptions, type UseSecretSequenceReturn, useSecretSequence };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
SecretSequence: () => SecretSequence,
|
|
24
|
+
useSecretSequence: () => useSecretSequence
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
|
|
28
|
+
// src/useSecretSequence.ts
|
|
29
|
+
var import_react = require("react");
|
|
30
|
+
var import_secret_sequence_core = require("secret-sequence-core");
|
|
31
|
+
function useSecretSequence(options) {
|
|
32
|
+
const {
|
|
33
|
+
sequences,
|
|
34
|
+
timeout,
|
|
35
|
+
enabled = true,
|
|
36
|
+
enableTouch = true,
|
|
37
|
+
ignoreInputs = true,
|
|
38
|
+
touchOptions
|
|
39
|
+
} = options;
|
|
40
|
+
const [progress, setProgress] = (0, import_react.useState)({});
|
|
41
|
+
const engineRef = (0, import_react.useRef)(null);
|
|
42
|
+
const sequencesRef = (0, import_react.useRef)(sequences);
|
|
43
|
+
sequencesRef.current = sequences;
|
|
44
|
+
const touchOptionsRef = (0, import_react.useRef)(touchOptions);
|
|
45
|
+
touchOptionsRef.current = touchOptions;
|
|
46
|
+
const sequencesKey = JSON.stringify(
|
|
47
|
+
sequences.map(({ onSuccess, ...rest }) => rest)
|
|
48
|
+
);
|
|
49
|
+
const touchOptionsKey = JSON.stringify(touchOptions);
|
|
50
|
+
(0, import_react.useEffect)(() => {
|
|
51
|
+
const engineOptions = {
|
|
52
|
+
sequences: sequencesRef.current,
|
|
53
|
+
timeout,
|
|
54
|
+
enabled,
|
|
55
|
+
enableTouch,
|
|
56
|
+
ignoreInputs,
|
|
57
|
+
touchOptions: touchOptionsRef.current,
|
|
58
|
+
onProgress: (_id, _step) => {
|
|
59
|
+
if (engineRef.current) {
|
|
60
|
+
setProgress(engineRef.current.getProgressMap());
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const engine = new import_secret_sequence_core.SecretSequenceEngine(engineOptions);
|
|
65
|
+
engineRef.current = engine;
|
|
66
|
+
setProgress(engine.getProgressMap());
|
|
67
|
+
engine.start();
|
|
68
|
+
return () => {
|
|
69
|
+
engine.destroy();
|
|
70
|
+
engineRef.current = null;
|
|
71
|
+
};
|
|
72
|
+
}, [timeout, enabled, enableTouch, ignoreInputs]);
|
|
73
|
+
(0, import_react.useEffect)(() => {
|
|
74
|
+
if (!engineRef.current) return;
|
|
75
|
+
engineRef.current.setOptions({
|
|
76
|
+
sequences: sequencesRef.current,
|
|
77
|
+
touchOptions: touchOptionsRef.current,
|
|
78
|
+
onProgress: () => {
|
|
79
|
+
if (engineRef.current) {
|
|
80
|
+
setProgress(engineRef.current.getProgressMap());
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
setProgress(engineRef.current.getProgressMap());
|
|
85
|
+
}, [sequencesKey, touchOptionsKey]);
|
|
86
|
+
const reset = (0, import_react.useCallback)(() => {
|
|
87
|
+
if (engineRef.current) {
|
|
88
|
+
engineRef.current.reset();
|
|
89
|
+
setProgress(engineRef.current.getProgressMap());
|
|
90
|
+
}
|
|
91
|
+
}, []);
|
|
92
|
+
return { progress, reset };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// src/SecretSequence.tsx
|
|
96
|
+
var import_react2 = require("react");
|
|
97
|
+
function SecretSequence(props) {
|
|
98
|
+
const {
|
|
99
|
+
onProgressChange,
|
|
100
|
+
children,
|
|
101
|
+
...hookOptions
|
|
102
|
+
} = props;
|
|
103
|
+
const { progress, reset } = useSecretSequence(hookOptions);
|
|
104
|
+
(0, import_react2.useEffect)(() => {
|
|
105
|
+
if (onProgressChange) {
|
|
106
|
+
onProgressChange(progress);
|
|
107
|
+
}
|
|
108
|
+
}, [progress, onProgressChange]);
|
|
109
|
+
if (typeof children === "function") {
|
|
110
|
+
return children({ progress, reset });
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
115
|
+
0 && (module.exports = {
|
|
116
|
+
SecretSequence,
|
|
117
|
+
useSecretSequence
|
|
118
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// src/useSecretSequence.ts
|
|
2
|
+
import { useEffect, useRef, useState, useCallback } from "react";
|
|
3
|
+
import {
|
|
4
|
+
SecretSequenceEngine
|
|
5
|
+
} from "secret-sequence-core";
|
|
6
|
+
function useSecretSequence(options) {
|
|
7
|
+
const {
|
|
8
|
+
sequences,
|
|
9
|
+
timeout,
|
|
10
|
+
enabled = true,
|
|
11
|
+
enableTouch = true,
|
|
12
|
+
ignoreInputs = true,
|
|
13
|
+
touchOptions
|
|
14
|
+
} = options;
|
|
15
|
+
const [progress, setProgress] = useState({});
|
|
16
|
+
const engineRef = useRef(null);
|
|
17
|
+
const sequencesRef = useRef(sequences);
|
|
18
|
+
sequencesRef.current = sequences;
|
|
19
|
+
const touchOptionsRef = useRef(touchOptions);
|
|
20
|
+
touchOptionsRef.current = touchOptions;
|
|
21
|
+
const sequencesKey = JSON.stringify(
|
|
22
|
+
sequences.map(({ onSuccess, ...rest }) => rest)
|
|
23
|
+
);
|
|
24
|
+
const touchOptionsKey = JSON.stringify(touchOptions);
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
const engineOptions = {
|
|
27
|
+
sequences: sequencesRef.current,
|
|
28
|
+
timeout,
|
|
29
|
+
enabled,
|
|
30
|
+
enableTouch,
|
|
31
|
+
ignoreInputs,
|
|
32
|
+
touchOptions: touchOptionsRef.current,
|
|
33
|
+
onProgress: (_id, _step) => {
|
|
34
|
+
if (engineRef.current) {
|
|
35
|
+
setProgress(engineRef.current.getProgressMap());
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
const engine = new SecretSequenceEngine(engineOptions);
|
|
40
|
+
engineRef.current = engine;
|
|
41
|
+
setProgress(engine.getProgressMap());
|
|
42
|
+
engine.start();
|
|
43
|
+
return () => {
|
|
44
|
+
engine.destroy();
|
|
45
|
+
engineRef.current = null;
|
|
46
|
+
};
|
|
47
|
+
}, [timeout, enabled, enableTouch, ignoreInputs]);
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
if (!engineRef.current) return;
|
|
50
|
+
engineRef.current.setOptions({
|
|
51
|
+
sequences: sequencesRef.current,
|
|
52
|
+
touchOptions: touchOptionsRef.current,
|
|
53
|
+
onProgress: () => {
|
|
54
|
+
if (engineRef.current) {
|
|
55
|
+
setProgress(engineRef.current.getProgressMap());
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
setProgress(engineRef.current.getProgressMap());
|
|
60
|
+
}, [sequencesKey, touchOptionsKey]);
|
|
61
|
+
const reset = useCallback(() => {
|
|
62
|
+
if (engineRef.current) {
|
|
63
|
+
engineRef.current.reset();
|
|
64
|
+
setProgress(engineRef.current.getProgressMap());
|
|
65
|
+
}
|
|
66
|
+
}, []);
|
|
67
|
+
return { progress, reset };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// src/SecretSequence.tsx
|
|
71
|
+
import { useEffect as useEffect2 } from "react";
|
|
72
|
+
function SecretSequence(props) {
|
|
73
|
+
const {
|
|
74
|
+
onProgressChange,
|
|
75
|
+
children,
|
|
76
|
+
...hookOptions
|
|
77
|
+
} = props;
|
|
78
|
+
const { progress, reset } = useSecretSequence(hookOptions);
|
|
79
|
+
useEffect2(() => {
|
|
80
|
+
if (onProgressChange) {
|
|
81
|
+
onProgressChange(progress);
|
|
82
|
+
}
|
|
83
|
+
}, [progress, onProgressChange]);
|
|
84
|
+
if (typeof children === "function") {
|
|
85
|
+
return children({ progress, reset });
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
export {
|
|
90
|
+
SecretSequence,
|
|
91
|
+
useSecretSequence
|
|
92
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "secret-sequence-react",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "React hook & component wrapper for secret-sequence-core — detect directional sequences, key combos, and touch gestures",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"react",
|
|
7
|
+
"hook",
|
|
8
|
+
"keyboard",
|
|
9
|
+
"stratagem",
|
|
10
|
+
"konami",
|
|
11
|
+
"sequence",
|
|
12
|
+
"shortcut",
|
|
13
|
+
"input-engine",
|
|
14
|
+
"helldivers",
|
|
15
|
+
"key-combo",
|
|
16
|
+
"easter-egg",
|
|
17
|
+
"typescript",
|
|
18
|
+
"touch",
|
|
19
|
+
"gesture"
|
|
20
|
+
],
|
|
21
|
+
"author": "diego0alonso",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/diego0alonso/secret-sequence.git",
|
|
26
|
+
"directory": "packages/react"
|
|
27
|
+
},
|
|
28
|
+
"main": "dist/index.js",
|
|
29
|
+
"module": "dist/index.mjs",
|
|
30
|
+
"types": "dist/index.d.ts",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"import": "./dist/index.mjs",
|
|
35
|
+
"require": "./dist/index.js"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"sideEffects": false,
|
|
39
|
+
"files": [
|
|
40
|
+
"dist"
|
|
41
|
+
],
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsup",
|
|
44
|
+
"prepublishOnly": "npm run build"
|
|
45
|
+
},
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"react": ">=17.0.0",
|
|
48
|
+
"secret-sequence-core": "^2.0.0"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/react": "^19.0.0",
|
|
52
|
+
"react": "^19.0.0",
|
|
53
|
+
"secret-sequence-core": "^2.0.0",
|
|
54
|
+
"tsup": "^8.0.0",
|
|
55
|
+
"typescript": "^5.0.0"
|
|
56
|
+
}
|
|
57
|
+
}
|