react-native-earl-gamepad 0.1.0 → 0.3.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 +94 -56
- package/dist/GamepadDebug.d.ts +1 -1
- package/dist/GamepadDebug.js +229 -118
- package/package.json +41 -41
package/README.md
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
# react-native-earl-gamepad
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
WebView-based gamepad bridge for React Native. Polls `navigator.getGamepads()` in a hidden WebView and surfaces buttons, sticks, d-pad, and connection events to JS.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
- Components: `GamepadBridge`, `useGamepad`, and `GamepadDebug`.
|
|
6
|
+
- Deadzone handling (default `0.15`) with auto-clear on disconnect.
|
|
7
|
+
- Typed events for buttons, axes, d-pad, and status.
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
9
|
+
## Requirements
|
|
10
|
+
|
|
11
|
+
- React Native `>=0.72`
|
|
12
|
+
- React `>=18`
|
|
13
|
+
- `react-native-webview` `>=13`
|
|
14
|
+
- Runs on iOS and Android (relies on WebView Gamepad API support).
|
|
10
15
|
|
|
11
16
|
## Install
|
|
12
17
|
|
|
@@ -16,91 +21,124 @@ npm install react-native-earl-gamepad react-native-webview
|
|
|
16
21
|
yarn add react-native-earl-gamepad react-native-webview
|
|
17
22
|
```
|
|
18
23
|
|
|
19
|
-
##
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
### Render the bridge (minimal)
|
|
27
|
+
|
|
28
|
+
Render the hidden WebView once in your tree to start polling the first connected pad (`navigator.getGamepads()[0]`).
|
|
20
29
|
|
|
21
30
|
```tsx
|
|
22
|
-
import { GamepadBridge } from
|
|
31
|
+
import { GamepadBridge } from "react-native-earl-gamepad";
|
|
23
32
|
|
|
24
33
|
export function Controls() {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
return (
|
|
35
|
+
<GamepadBridge
|
|
36
|
+
enabled
|
|
37
|
+
onButton={(e) =>
|
|
38
|
+
console.log("button", e.button, e.pressed, e.value)
|
|
39
|
+
}
|
|
40
|
+
onAxis={(e) => console.log("axis", e.axis, e.value)}
|
|
41
|
+
onDpad={(e) => console.log("dpad", e.key, e.pressed)}
|
|
42
|
+
onStatus={(e) => console.log("status", e.state)}
|
|
43
|
+
/>
|
|
44
|
+
);
|
|
34
45
|
}
|
|
35
46
|
```
|
|
36
47
|
|
|
37
|
-
###
|
|
48
|
+
### Hook for stateful consumption
|
|
49
|
+
|
|
50
|
+
`useGamepad` keeps pressed state and axes for you. You still need to render the provided `bridge` element once.
|
|
38
51
|
|
|
39
52
|
```tsx
|
|
40
|
-
import { useGamepad } from
|
|
53
|
+
import { useGamepad } from "react-native-earl-gamepad";
|
|
41
54
|
|
|
42
55
|
export function HUD() {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
56
|
+
const { pressedButtons, axes, isPressed, bridge } = useGamepad({
|
|
57
|
+
enabled: true,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<>
|
|
62
|
+
{bridge}
|
|
63
|
+
<Text>
|
|
64
|
+
Pressed: {Array.from(pressedButtons).join(", ") || "none"}
|
|
65
|
+
</Text>
|
|
66
|
+
<Text>
|
|
67
|
+
Left stick: x {axes.leftX?.toFixed(2)} / y{" "}
|
|
68
|
+
{axes.leftY?.toFixed(2)}
|
|
69
|
+
</Text>
|
|
70
|
+
<Text>A held? {isPressed("a") ? "yes" : "no"}</Text>
|
|
71
|
+
</>
|
|
72
|
+
);
|
|
55
73
|
}
|
|
56
74
|
```
|
|
57
75
|
|
|
58
76
|
### Visual debugger
|
|
59
77
|
|
|
78
|
+
Drop-in component to see a controller diagram that lights up buttons, shows stick offsets, and lists state.
|
|
79
|
+
|
|
60
80
|
```tsx
|
|
61
|
-
import { GamepadDebug } from
|
|
81
|
+
import { GamepadDebug } from "react-native-earl-gamepad";
|
|
62
82
|
|
|
63
83
|
export function DebugScreen() {
|
|
64
|
-
|
|
84
|
+
return <GamepadDebug axisThreshold={0.2} />;
|
|
65
85
|
}
|
|
66
86
|
```
|
|
67
87
|
|
|
68
|
-
`GamepadDebug` renders a controller diagram that lights up buttons, shows stick offsets, and lists pressed/axes values.
|
|
69
|
-
|
|
70
88
|
## API
|
|
71
89
|
|
|
72
90
|
### `GamepadBridge` props
|
|
73
91
|
|
|
74
|
-
-
|
|
75
|
-
-
|
|
76
|
-
-
|
|
77
|
-
-
|
|
78
|
-
-
|
|
79
|
-
-
|
|
80
|
-
-
|
|
92
|
+
- `enabled?: boolean` — mount/unmount the hidden WebView. Default `true`.
|
|
93
|
+
- `axisThreshold?: number` — deadzone applied to axes. Default `0.15`.
|
|
94
|
+
- `onButton?: (event: ButtonEvent) => void` — fired on button press/release/value change.
|
|
95
|
+
- `onAxis?: (event: AxisEvent) => void` — fired when an axis changes beyond threshold.
|
|
96
|
+
- `onDpad?: (event: DpadEvent) => void` — convenience mapping of button indices 12–15.
|
|
97
|
+
- `onStatus?: (event: StatusEvent) => void` — `connected` / `disconnected` events.
|
|
98
|
+
- `style?: StyleProp<ViewStyle>` — override container; default is a 1×1 transparent view.
|
|
81
99
|
|
|
82
|
-
### `useGamepad` return
|
|
100
|
+
### `useGamepad` options and return
|
|
83
101
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
- `
|
|
87
|
-
-
|
|
102
|
+
Options:
|
|
103
|
+
|
|
104
|
+
- `enabled?: boolean` — defaults to `true`. When false, state resets and axes zero out.
|
|
105
|
+
- `axisThreshold?: number` — deadzone for axes. Default `0.15`.
|
|
106
|
+
- `onButton`, `onAxis`, `onDpad`, `onStatus` — same semantics as `GamepadBridge`.
|
|
107
|
+
|
|
108
|
+
Return shape:
|
|
109
|
+
|
|
110
|
+
- `pressedButtons: Set<GamepadButtonName>` — current pressed buttons.
|
|
111
|
+
- `axes: Partial<Record<StickAxisName, number>>` — axis values with deadzone applied.
|
|
112
|
+
- `isPressed(key: GamepadButtonName): boolean` — helper to check a single button.
|
|
113
|
+
- `bridge: JSX.Element | null` — render once to enable polling.
|
|
88
114
|
|
|
89
115
|
### `GamepadDebug`
|
|
90
116
|
|
|
91
|
-
|
|
117
|
+
- `enabled?: boolean` — defaults to `true`.
|
|
118
|
+
- `axisThreshold?: number` — defaults to `0.15`.
|
|
119
|
+
|
|
120
|
+
## Events and types
|
|
121
|
+
|
|
122
|
+
- `ButtonEvent`: `{ type: 'button'; button: GamepadButtonName; index: number; pressed: boolean; value: number }`
|
|
123
|
+
- `AxisEvent`: `{ type: 'axis'; axis: StickAxisName; index: number; value: number }`
|
|
124
|
+
- `DpadEvent`: `{ type: 'dpad'; key: 'up' | 'down' | 'left' | 'right'; pressed: boolean }`
|
|
125
|
+
- `StatusEvent`: `{ type: 'status'; state: 'connected' | 'disconnected' }`
|
|
126
|
+
|
|
127
|
+
Button names map to the standard gamepad layout (`a`, `b`, `x`, `y`, `lb`, `rb`, `lt`, `rt`, `back`, `start`, `ls`, `rs`, `dpadUp`, `dpadDown`, `dpadLeft`, `dpadRight`, `home`). Unknown indices fall back to `button-N`. Axes map to `leftX`, `leftY`, `rightX`, `rightY` with fallbacks `axis-N`.
|
|
92
128
|
|
|
93
|
-
|
|
129
|
+
## Behavior notes
|
|
94
130
|
|
|
95
|
-
-
|
|
96
|
-
-
|
|
131
|
+
- Reads only the first controller (`navigator.getGamepads()[0]`).
|
|
132
|
+
- D-pad events mirror buttons 12–15; they emit separate `dpad` messages in addition to the raw button events.
|
|
133
|
+
- On disconnect, pressed state is cleared and release events are emitted so you do not get stuck buttons.
|
|
134
|
+
- Keep the bridge mounted; remounting clears internal state and can drop transient events.
|
|
135
|
+
- Axis values below the deadzone are coerced to `0`. Adjust `axisThreshold` if you need more sensitivity.
|
|
97
136
|
|
|
98
|
-
##
|
|
137
|
+
## Patterns
|
|
99
138
|
|
|
100
|
-
-
|
|
101
|
-
-
|
|
102
|
-
-
|
|
103
|
-
- The WebView must stay mounted; avoid remounting each render to prevent losing state.
|
|
139
|
+
- **Single place to render**: put the bridge near the root (e.g., inside your `App` provider layer) and consume state anywhere via `useGamepad`.
|
|
140
|
+
- **Status-aware UI**: use `onStatus` to disable controls until `connected` and to reset UI on `disconnected`.
|
|
141
|
+
- **Custom deadzone per screen**: pass `axisThreshold` to either the bridge or the hook depending on which you render.
|
|
104
142
|
|
|
105
143
|
## Development
|
|
106
144
|
|
|
@@ -109,7 +147,7 @@ npm install
|
|
|
109
147
|
npm run build
|
|
110
148
|
```
|
|
111
149
|
|
|
112
|
-
|
|
150
|
+
Build outputs to `dist/` with type declarations.
|
|
113
151
|
|
|
114
152
|
## License
|
|
115
153
|
|
package/dist/GamepadDebug.d.ts
CHANGED
|
@@ -2,5 +2,5 @@ type Props = {
|
|
|
2
2
|
enabled?: boolean;
|
|
3
3
|
axisThreshold?: number;
|
|
4
4
|
};
|
|
5
|
-
export default function GamepadDebug({ enabled, axisThreshold }: Props): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
export default function GamepadDebug({ enabled, axisThreshold, }: Props): import("react/jsx-runtime").JSX.Element;
|
|
6
6
|
export {};
|
package/dist/GamepadDebug.js
CHANGED
|
@@ -8,210 +8,321 @@ const jsx_runtime_1 = require("react/jsx-runtime");
|
|
|
8
8
|
const react_1 = require("react");
|
|
9
9
|
const react_native_1 = require("react-native");
|
|
10
10
|
const useGamepad_1 = __importDefault(require("./useGamepad"));
|
|
11
|
-
const trackedAxes = [
|
|
11
|
+
const trackedAxes = ["leftX", "leftY", "rightX", "rightY"];
|
|
12
12
|
const format = (value) => (value !== null && value !== void 0 ? value : 0).toFixed(2);
|
|
13
|
-
function GamepadDebug({ enabled = true, axisThreshold = 0.15 }) {
|
|
14
|
-
const { bridge, pressedButtons, axes } = (0, useGamepad_1.default)({
|
|
13
|
+
function GamepadDebug({ enabled = true, axisThreshold = 0.15, }) {
|
|
14
|
+
const { bridge, pressedButtons, axes } = (0, useGamepad_1.default)({
|
|
15
|
+
enabled,
|
|
16
|
+
axisThreshold,
|
|
17
|
+
});
|
|
15
18
|
const pressedList = (0, react_1.useMemo)(() => Array.from(pressedButtons).sort(), [pressedButtons]);
|
|
16
19
|
const pressed = (key) => pressedButtons.has(key);
|
|
17
20
|
const axis = (key) => { var _a; return (_a = axes[key]) !== null && _a !== void 0 ? _a : 0; };
|
|
18
|
-
return ((0, jsx_runtime_1.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
],
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
21
|
+
return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.container, children: (0, jsx_runtime_1.jsxs)(react_native_1.ScrollView, { contentContainerStyle: styles.scrollContent, showsVerticalScrollIndicator: false, children: [bridge, (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.headerRow, children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.title, children: "Gamepad Debug" }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.tag, children: (0, jsx_runtime_1.jsxs)(react_native_1.Text, { style: styles.tagText, children: ["Enabled ", enabled ? "On" : "Off"] }) })] }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.body, children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [styles.card, styles.controllerCard], children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.cardTitle, children: "Controller" }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.controller, children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.shoulders, children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
|
|
22
|
+
styles.bumper,
|
|
23
|
+
pressed("lb") && styles.active,
|
|
24
|
+
], children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.label, children: "LB" }) }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
|
|
25
|
+
styles.bumper,
|
|
26
|
+
pressed("rb") && styles.active,
|
|
27
|
+
], children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.label, children: "RB" }) })] }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.triggers, children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
|
|
28
|
+
styles.trigger,
|
|
29
|
+
pressed("lt") && styles.active,
|
|
30
|
+
], children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.label, children: "LT" }) }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
|
|
31
|
+
styles.trigger,
|
|
32
|
+
pressed("rt") && styles.active,
|
|
33
|
+
], children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.label, children: "RT" }) })] }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.midRow, children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.stickZone, children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.stickRing, children: (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
|
|
34
|
+
styles.stick,
|
|
35
|
+
{
|
|
36
|
+
transform: [
|
|
37
|
+
{
|
|
38
|
+
translateX: axis("leftX") *
|
|
39
|
+
20,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
translateY: axis("leftY") *
|
|
43
|
+
-20,
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
pressed("ls") && styles.active,
|
|
48
|
+
] }) }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.smallLabel, children: "LS" })] }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.centerCluster, children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
|
|
49
|
+
styles.smallKey,
|
|
50
|
+
pressed("back") && styles.active,
|
|
51
|
+
], children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.label, children: "Back" }) }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
|
|
52
|
+
styles.smallKey,
|
|
53
|
+
pressed("start") && styles.active,
|
|
54
|
+
], children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.label, children: "Start" }) }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
|
|
55
|
+
styles.smallKey,
|
|
56
|
+
pressed("home") && styles.active,
|
|
57
|
+
], children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.label, children: "Home" }) })] }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.faceCluster, children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.faceRow, children: (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
|
|
58
|
+
styles.faceButton,
|
|
59
|
+
pressed("y") &&
|
|
60
|
+
styles.faceActive,
|
|
61
|
+
], children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.faceText, children: "Y" }) }) }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.faceRow, children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
|
|
62
|
+
styles.faceButton,
|
|
63
|
+
pressed("x") &&
|
|
64
|
+
styles.faceActive,
|
|
65
|
+
], children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.faceText, children: "X" }) }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
|
|
66
|
+
styles.faceButton,
|
|
67
|
+
pressed("b") &&
|
|
68
|
+
styles.faceActive,
|
|
69
|
+
], children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.faceText, children: "B" }) })] }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.faceRow, children: (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
|
|
70
|
+
styles.faceButton,
|
|
71
|
+
pressed("a") &&
|
|
72
|
+
styles.faceActive,
|
|
73
|
+
], children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.faceText, children: "A" }) }) })] })] }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.bottomRow, children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.dpad, children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
|
|
74
|
+
styles.dpadKey,
|
|
75
|
+
styles.dpadVertical,
|
|
76
|
+
styles.dpadUp,
|
|
77
|
+
pressed("dpadUp") && styles.active,
|
|
78
|
+
] }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
|
|
79
|
+
styles.dpadKey,
|
|
80
|
+
styles.dpadVertical,
|
|
81
|
+
styles.dpadDown,
|
|
82
|
+
pressed("dpadDown") &&
|
|
83
|
+
styles.active,
|
|
84
|
+
] }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
|
|
85
|
+
styles.dpadKey,
|
|
86
|
+
styles.dpadHorizontal,
|
|
87
|
+
styles.dpadLeft,
|
|
88
|
+
pressed("dpadLeft") &&
|
|
89
|
+
styles.active,
|
|
90
|
+
] }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
|
|
91
|
+
styles.dpadKey,
|
|
92
|
+
styles.dpadHorizontal,
|
|
93
|
+
styles.dpadRight,
|
|
94
|
+
pressed("dpadRight") &&
|
|
95
|
+
styles.active,
|
|
96
|
+
] })] }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.stickZone, children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.stickRing, children: (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
|
|
97
|
+
styles.stick,
|
|
98
|
+
{
|
|
99
|
+
transform: [
|
|
100
|
+
{
|
|
101
|
+
translateX: axis("rightX") *
|
|
102
|
+
20,
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
translateY: axis("rightY") *
|
|
106
|
+
-20,
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
},
|
|
110
|
+
pressed("rs") && styles.active,
|
|
111
|
+
] }) }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.smallLabel, children: "RS" })] })] })] })] }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [styles.card, styles.stateCard], children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.cardTitle, children: "State" }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.sectionTitle, children: "Pressed" }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.badgeRow, children: pressedList.length === 0 ? ((0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.muted, children: "None" })) : (pressedList.map((name) => ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.badge, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.badgeText, children: name }) }, name)))) }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.sectionTitle, children: "Axes" }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.axesGrid, children: trackedAxes.map((axisName) => ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.axisItem, children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.axisLabel, children: axisName }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.axisValue, children: format(axes[axisName]) })] }, axisName))) })] })] })] }) }));
|
|
57
112
|
}
|
|
58
113
|
const styles = react_native_1.StyleSheet.create({
|
|
59
114
|
container: {
|
|
115
|
+
flex: 1,
|
|
116
|
+
width: "100%",
|
|
60
117
|
padding: 12,
|
|
118
|
+
backgroundColor: "#f5f7fb",
|
|
119
|
+
},
|
|
120
|
+
scrollContent: {
|
|
121
|
+
paddingBottom: 24,
|
|
61
122
|
gap: 12,
|
|
62
|
-
backgroundColor: '#0b1220',
|
|
63
|
-
borderRadius: 12,
|
|
64
|
-
borderWidth: 1,
|
|
65
|
-
borderColor: '#1f2a3d',
|
|
66
123
|
},
|
|
67
124
|
title: {
|
|
68
125
|
fontSize: 18,
|
|
69
|
-
fontWeight:
|
|
70
|
-
color:
|
|
126
|
+
fontWeight: "700",
|
|
127
|
+
color: "#0f172a",
|
|
128
|
+
},
|
|
129
|
+
headerRow: {
|
|
130
|
+
flexDirection: "row",
|
|
131
|
+
justifyContent: "space-between",
|
|
132
|
+
alignItems: "center",
|
|
133
|
+
gap: 12,
|
|
134
|
+
},
|
|
135
|
+
tag: {
|
|
136
|
+
backgroundColor: "#e2e8f0",
|
|
137
|
+
paddingVertical: 6,
|
|
138
|
+
paddingHorizontal: 12,
|
|
139
|
+
borderRadius: 999,
|
|
140
|
+
borderWidth: 1,
|
|
141
|
+
borderColor: "#cbd5e1",
|
|
142
|
+
},
|
|
143
|
+
tagText: {
|
|
144
|
+
color: "#0f172a",
|
|
145
|
+
fontWeight: "700",
|
|
146
|
+
fontSize: 12,
|
|
71
147
|
},
|
|
72
148
|
subtitle: {
|
|
73
149
|
marginTop: 4,
|
|
74
150
|
fontSize: 14,
|
|
75
|
-
fontWeight:
|
|
76
|
-
color:
|
|
151
|
+
fontWeight: "600",
|
|
152
|
+
color: "#475569",
|
|
77
153
|
},
|
|
78
154
|
muted: {
|
|
79
|
-
color:
|
|
155
|
+
color: "#64748b",
|
|
80
156
|
fontSize: 13,
|
|
81
157
|
},
|
|
82
158
|
badgeRow: {
|
|
83
|
-
flexDirection:
|
|
84
|
-
flexWrap:
|
|
159
|
+
flexDirection: "row",
|
|
160
|
+
flexWrap: "wrap",
|
|
85
161
|
gap: 6,
|
|
86
162
|
},
|
|
87
163
|
badge: {
|
|
88
164
|
paddingVertical: 4,
|
|
89
165
|
paddingHorizontal: 8,
|
|
90
166
|
borderRadius: 12,
|
|
91
|
-
backgroundColor:
|
|
167
|
+
backgroundColor: "#e2e8f0",
|
|
92
168
|
borderWidth: 1,
|
|
93
|
-
borderColor:
|
|
169
|
+
borderColor: "#cbd5e1",
|
|
94
170
|
},
|
|
95
171
|
badgeText: {
|
|
96
|
-
color:
|
|
172
|
+
color: "#0f172a",
|
|
97
173
|
fontSize: 12,
|
|
98
|
-
fontWeight:
|
|
174
|
+
fontWeight: "600",
|
|
175
|
+
},
|
|
176
|
+
sectionTitle: {
|
|
177
|
+
marginTop: 12,
|
|
178
|
+
marginBottom: 6,
|
|
179
|
+
color: "#0f172a",
|
|
180
|
+
fontSize: 13,
|
|
181
|
+
fontWeight: "700",
|
|
99
182
|
},
|
|
100
183
|
axisItem: {
|
|
101
184
|
padding: 8,
|
|
102
185
|
borderRadius: 8,
|
|
103
|
-
backgroundColor:
|
|
186
|
+
backgroundColor: "#f8fafc",
|
|
104
187
|
borderWidth: 1,
|
|
105
|
-
borderColor:
|
|
188
|
+
borderColor: "#cbd5e1",
|
|
106
189
|
minWidth: 90,
|
|
107
190
|
},
|
|
108
191
|
axisLabel: {
|
|
109
|
-
color:
|
|
192
|
+
color: "#0f172a",
|
|
110
193
|
fontSize: 12,
|
|
111
|
-
fontWeight:
|
|
194
|
+
fontWeight: "600",
|
|
112
195
|
},
|
|
113
196
|
axisValue: {
|
|
114
|
-
color:
|
|
197
|
+
color: "#0f172a",
|
|
198
|
+
fontSize: 16,
|
|
199
|
+
fontVariant: ["tabular-nums"],
|
|
200
|
+
},
|
|
201
|
+
body: {
|
|
202
|
+
flexDirection: "row",
|
|
203
|
+
flexWrap: "wrap",
|
|
204
|
+
gap: 12,
|
|
205
|
+
alignSelf: "stretch",
|
|
206
|
+
},
|
|
207
|
+
card: {
|
|
208
|
+
flexGrow: 1,
|
|
209
|
+
flexShrink: 1,
|
|
210
|
+
flexBasis: "100%",
|
|
211
|
+
backgroundColor: "#ffffff",
|
|
212
|
+
borderRadius: 14,
|
|
213
|
+
borderWidth: 1,
|
|
214
|
+
borderColor: "#e2e8f0",
|
|
215
|
+
padding: 12,
|
|
216
|
+
gap: 12,
|
|
217
|
+
},
|
|
218
|
+
controllerCard: {
|
|
219
|
+
minWidth: 320,
|
|
220
|
+
},
|
|
221
|
+
stateCard: {
|
|
222
|
+
minWidth: 260,
|
|
223
|
+
},
|
|
224
|
+
cardTitle: {
|
|
225
|
+
color: "#0f172a",
|
|
226
|
+
fontWeight: "700",
|
|
115
227
|
fontSize: 16,
|
|
116
|
-
fontVariant: ['tabular-nums'],
|
|
117
228
|
},
|
|
118
229
|
controller: {
|
|
119
|
-
backgroundColor:
|
|
230
|
+
backgroundColor: "#f8fafc",
|
|
120
231
|
borderRadius: 14,
|
|
121
232
|
borderWidth: 1,
|
|
122
|
-
borderColor:
|
|
233
|
+
borderColor: "#e2e8f0",
|
|
123
234
|
padding: 12,
|
|
124
235
|
gap: 12,
|
|
125
236
|
},
|
|
126
237
|
shoulders: {
|
|
127
|
-
flexDirection:
|
|
128
|
-
justifyContent:
|
|
238
|
+
flexDirection: "row",
|
|
239
|
+
justifyContent: "space-between",
|
|
129
240
|
},
|
|
130
241
|
triggers: {
|
|
131
|
-
flexDirection:
|
|
132
|
-
justifyContent:
|
|
242
|
+
flexDirection: "row",
|
|
243
|
+
justifyContent: "space-between",
|
|
133
244
|
gap: 8,
|
|
134
245
|
},
|
|
135
246
|
bumper: {
|
|
136
247
|
flex: 1,
|
|
137
248
|
paddingVertical: 8,
|
|
138
249
|
borderRadius: 10,
|
|
139
|
-
backgroundColor:
|
|
250
|
+
backgroundColor: "#e2e8f0",
|
|
140
251
|
borderWidth: 1,
|
|
141
|
-
borderColor:
|
|
142
|
-
alignItems:
|
|
252
|
+
borderColor: "#cbd5e1",
|
|
253
|
+
alignItems: "center",
|
|
143
254
|
},
|
|
144
255
|
trigger: {
|
|
145
256
|
flex: 1,
|
|
146
257
|
paddingVertical: 8,
|
|
147
258
|
borderRadius: 10,
|
|
148
|
-
backgroundColor:
|
|
259
|
+
backgroundColor: "#e2e8f0",
|
|
149
260
|
borderWidth: 1,
|
|
150
|
-
borderColor:
|
|
151
|
-
alignItems:
|
|
261
|
+
borderColor: "#cbd5e1",
|
|
262
|
+
alignItems: "center",
|
|
152
263
|
},
|
|
153
264
|
label: {
|
|
154
|
-
color:
|
|
265
|
+
color: "#0f172a",
|
|
155
266
|
fontSize: 12,
|
|
156
|
-
fontWeight:
|
|
267
|
+
fontWeight: "700",
|
|
157
268
|
},
|
|
158
269
|
smallLabel: {
|
|
159
|
-
color:
|
|
270
|
+
color: "#475569",
|
|
160
271
|
fontSize: 11,
|
|
161
272
|
marginTop: 4,
|
|
162
|
-
textAlign:
|
|
273
|
+
textAlign: "center",
|
|
163
274
|
},
|
|
164
275
|
midRow: {
|
|
165
|
-
flexDirection:
|
|
166
|
-
alignItems:
|
|
167
|
-
justifyContent:
|
|
276
|
+
flexDirection: "row",
|
|
277
|
+
alignItems: "center",
|
|
278
|
+
justifyContent: "space-between",
|
|
168
279
|
gap: 12,
|
|
169
280
|
},
|
|
170
281
|
centerCluster: {
|
|
171
|
-
alignItems:
|
|
282
|
+
alignItems: "center",
|
|
172
283
|
gap: 6,
|
|
173
284
|
},
|
|
174
285
|
smallKey: {
|
|
175
286
|
paddingVertical: 6,
|
|
176
287
|
paddingHorizontal: 10,
|
|
177
288
|
borderRadius: 8,
|
|
178
|
-
backgroundColor:
|
|
289
|
+
backgroundColor: "#e2e8f0",
|
|
179
290
|
borderWidth: 1,
|
|
180
|
-
borderColor:
|
|
291
|
+
borderColor: "#cbd5e1",
|
|
181
292
|
},
|
|
182
293
|
faceCluster: {
|
|
183
|
-
alignItems:
|
|
294
|
+
alignItems: "center",
|
|
184
295
|
gap: 4,
|
|
185
296
|
},
|
|
186
297
|
faceRow: {
|
|
187
|
-
flexDirection:
|
|
298
|
+
flexDirection: "row",
|
|
188
299
|
gap: 8,
|
|
189
|
-
justifyContent:
|
|
300
|
+
justifyContent: "center",
|
|
190
301
|
},
|
|
191
302
|
faceButton: {
|
|
192
303
|
width: 42,
|
|
193
304
|
height: 42,
|
|
194
305
|
borderRadius: 21,
|
|
195
|
-
backgroundColor:
|
|
306
|
+
backgroundColor: "#e0f2fe",
|
|
196
307
|
borderWidth: 1,
|
|
197
|
-
borderColor:
|
|
198
|
-
alignItems:
|
|
199
|
-
justifyContent:
|
|
308
|
+
borderColor: "#bae6fd",
|
|
309
|
+
alignItems: "center",
|
|
310
|
+
justifyContent: "center",
|
|
200
311
|
},
|
|
201
312
|
faceText: {
|
|
202
|
-
color:
|
|
203
|
-
fontWeight:
|
|
313
|
+
color: "#0f172a",
|
|
314
|
+
fontWeight: "800",
|
|
204
315
|
},
|
|
205
316
|
faceActive: {
|
|
206
|
-
backgroundColor:
|
|
207
|
-
borderColor:
|
|
208
|
-
shadowColor:
|
|
317
|
+
backgroundColor: "#2563eb",
|
|
318
|
+
borderColor: "#1d4ed8",
|
|
319
|
+
shadowColor: "#2563eb",
|
|
209
320
|
shadowOpacity: 0.6,
|
|
210
321
|
shadowRadius: 6,
|
|
211
322
|
},
|
|
212
323
|
stickZone: {
|
|
213
324
|
width: 96,
|
|
214
|
-
alignItems:
|
|
325
|
+
alignItems: "center",
|
|
215
326
|
gap: 6,
|
|
216
327
|
},
|
|
217
328
|
stickRing: {
|
|
@@ -219,36 +330,36 @@ const styles = react_native_1.StyleSheet.create({
|
|
|
219
330
|
height: 84,
|
|
220
331
|
borderRadius: 42,
|
|
221
332
|
borderWidth: 1,
|
|
222
|
-
borderColor:
|
|
223
|
-
backgroundColor:
|
|
224
|
-
alignItems:
|
|
225
|
-
justifyContent:
|
|
333
|
+
borderColor: "#cbd5e1",
|
|
334
|
+
backgroundColor: "#f8fafc",
|
|
335
|
+
alignItems: "center",
|
|
336
|
+
justifyContent: "center",
|
|
226
337
|
},
|
|
227
338
|
stick: {
|
|
228
339
|
width: 38,
|
|
229
340
|
height: 38,
|
|
230
341
|
borderRadius: 19,
|
|
231
|
-
backgroundColor:
|
|
342
|
+
backgroundColor: "#e2e8f0",
|
|
232
343
|
borderWidth: 2,
|
|
233
|
-
borderColor:
|
|
344
|
+
borderColor: "#cbd5e1",
|
|
234
345
|
},
|
|
235
346
|
bottomRow: {
|
|
236
|
-
flexDirection:
|
|
237
|
-
justifyContent:
|
|
238
|
-
alignItems:
|
|
347
|
+
flexDirection: "row",
|
|
348
|
+
justifyContent: "space-between",
|
|
349
|
+
alignItems: "center",
|
|
239
350
|
gap: 12,
|
|
240
351
|
},
|
|
241
352
|
dpad: {
|
|
242
353
|
width: 120,
|
|
243
354
|
height: 120,
|
|
244
|
-
alignItems:
|
|
245
|
-
justifyContent:
|
|
246
|
-
position:
|
|
355
|
+
alignItems: "center",
|
|
356
|
+
justifyContent: "center",
|
|
357
|
+
position: "relative",
|
|
247
358
|
},
|
|
248
359
|
dpadKey: {
|
|
249
|
-
position:
|
|
250
|
-
backgroundColor:
|
|
251
|
-
borderColor:
|
|
360
|
+
position: "absolute",
|
|
361
|
+
backgroundColor: "#e2e8f0",
|
|
362
|
+
borderColor: "#cbd5e1",
|
|
252
363
|
borderWidth: 1,
|
|
253
364
|
borderRadius: 6,
|
|
254
365
|
},
|
|
@@ -261,9 +372,9 @@ const styles = react_native_1.StyleSheet.create({
|
|
|
261
372
|
height: 28,
|
|
262
373
|
},
|
|
263
374
|
active: {
|
|
264
|
-
borderColor:
|
|
265
|
-
backgroundColor:
|
|
266
|
-
shadowColor:
|
|
375
|
+
borderColor: "#2563eb",
|
|
376
|
+
backgroundColor: "#2563eb",
|
|
377
|
+
shadowColor: "#2563eb",
|
|
267
378
|
shadowOpacity: 0.5,
|
|
268
379
|
shadowRadius: 4,
|
|
269
380
|
},
|
|
@@ -272,8 +383,8 @@ const styles = react_native_1.StyleSheet.create({
|
|
|
272
383
|
dpadLeft: { left: 6, top: 46 },
|
|
273
384
|
dpadRight: { right: 6, top: 46 },
|
|
274
385
|
axesGrid: {
|
|
275
|
-
flexDirection:
|
|
276
|
-
flexWrap:
|
|
386
|
+
flexDirection: "row",
|
|
387
|
+
flexWrap: "wrap",
|
|
277
388
|
gap: 8,
|
|
278
389
|
},
|
|
279
390
|
});
|
package/package.json
CHANGED
|
@@ -1,43 +1,43 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
2
|
+
"name": "react-native-earl-gamepad",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "React Native gamepad bridge via WebView (buttons, sticks, d-pad, status).",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"README.md"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc -p .",
|
|
13
|
+
"clean": "rimraf dist"
|
|
14
|
+
},
|
|
15
|
+
"peerDependencies": {
|
|
16
|
+
"react": ">=18",
|
|
17
|
+
"react-native": ">=0.72",
|
|
18
|
+
"react-native-webview": ">=13"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/react": "^19.0.0",
|
|
22
|
+
"react": "^19.0.0",
|
|
23
|
+
"react-native": "^0.81.5",
|
|
24
|
+
"react-native-webview": "^13.12.2",
|
|
25
|
+
"typescript": "^5.3.3"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"react-native",
|
|
29
|
+
"gamepad",
|
|
30
|
+
"webview",
|
|
31
|
+
"controller"
|
|
32
|
+
],
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/Swif7ify/react-native-earl-gamepad.git"
|
|
36
|
+
},
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/Swif7ify/react-native-earl-gamepad/issues"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/Swif7ify/react-native-earl-gamepad#readme",
|
|
41
|
+
"author": "Ordovez, Earl Romeo",
|
|
42
|
+
"license": "MIT"
|
|
43
43
|
}
|