react-native-earl-gamepad 0.3.0 → 0.4.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 CHANGED
@@ -85,6 +85,10 @@ export function DebugScreen() {
85
85
  }
86
86
  ```
87
87
 
88
+ ![Gamepad visual idle](https://github.com/user-attachments/assets/dfebd8c5-7d9a-42c7-802b-2773ec8c8ae9)
89
+
90
+ ![Gamepad visual pressed](https://github.com/user-attachments/assets/7b37d76a-7695-4be9-bda4-7e3d1e6adf41)
91
+
88
92
  ## API
89
93
 
90
94
  ### `GamepadBridge` props
@@ -109,6 +113,7 @@ Return shape:
109
113
 
110
114
  - `pressedButtons: Set<GamepadButtonName>` — current pressed buttons.
111
115
  - `axes: Partial<Record<StickAxisName, number>>` — axis values with deadzone applied.
116
+ - `buttonValues: Partial<Record<GamepadButtonName, number>>` — last analog value per button (useful for LT/RT triggers).
112
117
  - `isPressed(key: GamepadButtonName): boolean` — helper to check a single button.
113
118
  - `bridge: JSX.Element | null` — render once to enable polling.
114
119
 
@@ -133,6 +138,7 @@ Button names map to the standard gamepad layout (`a`, `b`, `x`, `y`, `lb`, `rb`,
133
138
  - On disconnect, pressed state is cleared and release events are emitted so you do not get stuck buttons.
134
139
  - Keep the bridge mounted; remounting clears internal state and can drop transient events.
135
140
  - Axis values below the deadzone are coerced to `0`. Adjust `axisThreshold` if you need more sensitivity.
141
+ - LT/RT expose analog values via `buttonValues.lt` and `buttonValues.rt`.
136
142
 
137
143
  ## Patterns
138
144
 
@@ -10,111 +10,163 @@ const react_native_1 = require("react-native");
10
10
  const useGamepad_1 = __importDefault(require("./useGamepad"));
11
11
  const trackedAxes = ["leftX", "leftY", "rightX", "rightY"];
12
12
  const format = (value) => (value !== null && value !== void 0 ? value : 0).toFixed(2);
13
+ function ControllerVisual({ pressed, axis, value }) {
14
+ const level = (name) => {
15
+ const v = value(name);
16
+ return Math.max(v, pressed(name) ? 1 : 0);
17
+ };
18
+ const mix = (a, b, t) => Math.round(a + (b - a) * t);
19
+ const stickColor = (mag) => {
20
+ const clamped = Math.min(1, Math.max(0, mag));
21
+ const r = mix(34, 37, clamped);
22
+ const g = mix(34, 99, clamped);
23
+ const b = mix(34, 235, clamped);
24
+ return `rgb(${r}, ${g}, ${b})`;
25
+ };
26
+ const stickInner = (mag) => {
27
+ const clamped = Math.min(1, Math.max(0, mag));
28
+ const shade = mix(109, 200, clamped);
29
+ return `rgb(${shade}, ${shade}, ${shade})`;
30
+ };
31
+ const leftMag = Math.min(1, Math.hypot(axis("leftX"), axis("leftY")));
32
+ const rightMag = Math.min(1, Math.hypot(axis("rightX"), axis("rightY")));
33
+ return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.psWrapper, children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.psContainer, children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.psShoulderRow, children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
34
+ styles.psShoulder,
35
+ styles.psShoulderLeft,
36
+ level("lb") > 0 && styles.psShoulderActive,
37
+ { opacity: 0.55 + 0.45 * level("lb") },
38
+ ], children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [
39
+ styles.psShoulderText,
40
+ level("lb") > 0 && styles.psShoulderTextActive,
41
+ ], children: "L1" }) }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
42
+ styles.psShoulder,
43
+ styles.psShoulderLeft,
44
+ level("lt") > 0 && styles.psShoulderActive,
45
+ { opacity: 0.55 + 0.45 * level("lt") },
46
+ ], children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [
47
+ styles.psShoulderText,
48
+ level("lt") > 0 && styles.psShoulderTextActive,
49
+ ], children: "L2" }) }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
50
+ styles.psShoulder,
51
+ styles.psShoulderRight,
52
+ level("rb") > 0 && styles.psShoulderActive,
53
+ { opacity: 0.55 + 0.45 * level("rb") },
54
+ ], children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [
55
+ styles.psShoulderText,
56
+ level("rb") > 0 && styles.psShoulderTextActive,
57
+ ], children: "R1" }) }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
58
+ styles.psShoulder,
59
+ styles.psShoulderRight,
60
+ level("rt") > 0 && styles.psShoulderActive,
61
+ { opacity: 0.55 + 0.45 * level("rt") },
62
+ ], children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [
63
+ styles.psShoulderText,
64
+ level("rt") > 0 && styles.psShoulderTextActive,
65
+ ], children: "R2" }) })] }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.psMiddle }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.psPaveTactile }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
66
+ styles.psShare,
67
+ pressed("back") && styles.psActiveButton,
68
+ ] }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
69
+ styles.psOptions,
70
+ pressed("start") && styles.psActiveButton,
71
+ ] }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.psLeftHand, children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.psLeftPad, children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
72
+ styles.psArrowUp,
73
+ pressed("dpadUp") && styles.psActiveButton,
74
+ ] }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
75
+ styles.psArrowDown,
76
+ pressed("dpadDown") && styles.psActiveButton,
77
+ ] }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
78
+ styles.psArrowRight,
79
+ pressed("dpadRight") && styles.psActiveButton,
80
+ ] }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
81
+ styles.psArrowLeft,
82
+ pressed("dpadLeft") && styles.psActiveButton,
83
+ ] })] }) }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.psRightHand, children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.psRightPad, children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
84
+ styles.psTriangle,
85
+ pressed("y") && styles.psActiveButton,
86
+ ] }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
87
+ styles.psTriangleBas,
88
+ pressed("y") && styles.psTriangleLineActive,
89
+ ] }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
90
+ styles.psTriangleGauche,
91
+ pressed("y") && styles.psTriangleLineActive,
92
+ ] }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
93
+ styles.psTriangleDroit,
94
+ pressed("y") && styles.psTriangleLineActive,
95
+ ] }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
96
+ styles.psCarre,
97
+ pressed("x") && styles.psActiveButton,
98
+ ], children: (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
99
+ styles.psCarreRose,
100
+ pressed("x") && styles.psPinkActive,
101
+ ] }) }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
102
+ styles.psRond,
103
+ pressed("b") && styles.psActiveButton,
104
+ ], children: (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
105
+ styles.psRondRouge,
106
+ pressed("b") && styles.psRedActive,
107
+ ] }) }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [
108
+ styles.psCroix,
109
+ pressed("a") && styles.psActiveButton,
110
+ ], children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
111
+ styles.psCroixBleue,
112
+ pressed("a") && styles.psCrossLineActive,
113
+ ] }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
114
+ styles.psCroixBleue2,
115
+ pressed("a") && styles.psCrossLineActive,
116
+ ] })] })] }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
117
+ styles.psRollLeft,
118
+ pressed("ls") && styles.psStickPressed,
119
+ ], children: (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
120
+ styles.psRollIn,
121
+ {
122
+ transform: [
123
+ { translateX: axis("leftX") * 6 },
124
+ { translateY: axis("leftY") * -6 },
125
+ ],
126
+ backgroundColor: stickColor(leftMag),
127
+ },
128
+ pressed("ls") && styles.psStickInnerPressed,
129
+ ], children: (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
130
+ styles.psRollInIn,
131
+ { backgroundColor: stickInner(leftMag) },
132
+ pressed("ls") && styles.psStickCenterPressed,
133
+ ] }) }) }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
134
+ styles.psPsButton,
135
+ pressed("home") && styles.psActiveButton,
136
+ ] }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
137
+ styles.psRollRight,
138
+ pressed("rs") && styles.psStickPressed,
139
+ ], children: (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
140
+ styles.psRollIn,
141
+ {
142
+ transform: [
143
+ { translateX: axis("rightX") * 6 },
144
+ { translateY: axis("rightY") * -6 },
145
+ ],
146
+ backgroundColor: stickColor(rightMag),
147
+ },
148
+ pressed("rs") && styles.psStickInnerPressed,
149
+ ], children: (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
150
+ styles.psRollInIn,
151
+ { backgroundColor: stickInner(rightMag) },
152
+ pressed("rs") && styles.psStickCenterPressed,
153
+ ] }) }) })] })] }) }));
154
+ }
13
155
  function GamepadDebug({ enabled = true, axisThreshold = 0.15, }) {
14
- const { bridge, pressedButtons, axes } = (0, useGamepad_1.default)({
156
+ const { bridge, pressedButtons, axes, buttonValues } = (0, useGamepad_1.default)({
15
157
  enabled,
16
158
  axisThreshold,
17
159
  });
18
160
  const pressedList = (0, react_1.useMemo)(() => Array.from(pressedButtons).sort(), [pressedButtons]);
19
161
  const pressed = (key) => pressedButtons.has(key);
20
- const axis = (key) => { var _a; return (_a = axes[key]) !== null && _a !== void 0 ? _a : 0; };
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))) })] })] })] }) }));
162
+ const axisValue = (key) => { var _a; return (_a = axes[key]) !== null && _a !== void 0 ? _a : 0; };
163
+ const buttonValue = (key) => { var _a; return (_a = buttonValues[key]) !== null && _a !== void 0 ? _a : 0; };
164
+ 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.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.jsx)(react_native_1.View, { style: styles.controller, children: (0, jsx_runtime_1.jsx)(ControllerVisual, { pressed: pressed, axis: axisValue, value: buttonValue }) })] }), (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))) })] })] })] }) }));
112
165
  }
113
166
  const styles = react_native_1.StyleSheet.create({
114
167
  container: {
115
168
  flex: 1,
116
169
  width: "100%",
117
- padding: 12,
118
170
  backgroundColor: "#f5f7fb",
119
171
  },
120
172
  scrollContent: {
@@ -209,7 +261,6 @@ const styles = react_native_1.StyleSheet.create({
209
261
  flexShrink: 1,
210
262
  flexBasis: "100%",
211
263
  backgroundColor: "#ffffff",
212
- borderRadius: 14,
213
264
  borderWidth: 1,
214
265
  borderColor: "#e2e8f0",
215
266
  padding: 12,
@@ -228,163 +279,471 @@ const styles = react_native_1.StyleSheet.create({
228
279
  },
229
280
  controller: {
230
281
  backgroundColor: "#f8fafc",
231
- borderRadius: 14,
232
282
  borderWidth: 1,
233
283
  borderColor: "#e2e8f0",
234
284
  padding: 12,
235
- gap: 12,
285
+ alignItems: "center",
286
+ justifyContent: "center",
236
287
  },
237
- shoulders: {
288
+ axesGrid: {
238
289
  flexDirection: "row",
239
- justifyContent: "space-between",
290
+ flexWrap: "wrap",
291
+ gap: 8,
292
+ },
293
+ psWrapper: {
294
+ alignItems: "center",
295
+ justifyContent: "center",
240
296
  },
241
- triggers: {
297
+ psShoulderRow: {
298
+ position: "absolute",
299
+ top: 10,
300
+ left: 0,
301
+ right: 0,
242
302
  flexDirection: "row",
243
303
  justifyContent: "space-between",
244
- gap: 8,
304
+ paddingHorizontal: 24,
305
+ zIndex: 25,
245
306
  },
246
- bumper: {
247
- flex: 1,
307
+ psShoulder: {
308
+ minWidth: 62,
248
309
  paddingVertical: 8,
249
- borderRadius: 10,
310
+ paddingHorizontal: 14,
311
+ borderRadius: 999,
250
312
  backgroundColor: "#e2e8f0",
251
313
  borderWidth: 1,
252
314
  borderColor: "#cbd5e1",
253
315
  alignItems: "center",
316
+ shadowColor: "#000",
317
+ shadowOffset: { width: 0, height: 2 },
318
+ shadowOpacity: 0.25,
319
+ shadowRadius: 6,
320
+ elevation: 4,
254
321
  },
255
- trigger: {
256
- flex: 1,
257
- paddingVertical: 8,
258
- borderRadius: 10,
259
- backgroundColor: "#e2e8f0",
260
- borderWidth: 1,
261
- borderColor: "#cbd5e1",
262
- alignItems: "center",
322
+ psShoulderLeft: {
323
+ marginRight: 6,
263
324
  },
264
- label: {
325
+ psShoulderRight: {
326
+ marginLeft: 6,
327
+ },
328
+ psShoulderActive: {
329
+ backgroundColor: "#2563eb",
330
+ borderColor: "#1e3a8a",
331
+ shadowColor: "#2563eb",
332
+ shadowOpacity: 0.5,
333
+ },
334
+ psShoulderText: {
265
335
  color: "#0f172a",
266
- fontSize: 12,
267
336
  fontWeight: "700",
268
337
  },
269
- smallLabel: {
270
- color: "#475569",
271
- fontSize: 11,
272
- marginTop: 4,
273
- textAlign: "center",
338
+ psShoulderTextActive: {
339
+ color: "#e0f2fe",
274
340
  },
275
- midRow: {
276
- flexDirection: "row",
277
- alignItems: "center",
278
- justifyContent: "space-between",
279
- gap: 12,
341
+ psContainer: {
342
+ position: "relative",
343
+ width: 500,
344
+ height: 350,
345
+ },
346
+ psMiddle: {
347
+ width: 395,
348
+ height: 160,
349
+ backgroundColor: "#e0e0e0",
350
+ marginLeft: 27,
351
+ borderRadius: 25,
352
+ position: "absolute",
353
+ zIndex: 0,
354
+ top: 80,
355
+ },
356
+ psPaveTactile: {
357
+ width: 150,
358
+ height: 80,
359
+ backgroundColor: "#333",
360
+ marginLeft: 147,
361
+ borderRadius: 7,
362
+ position: "absolute",
363
+ zIndex: 10,
364
+ top: 80,
365
+ shadowColor: "#000",
366
+ shadowOffset: { width: 0, height: 0 },
367
+ shadowOpacity: 0.5,
368
+ shadowRadius: 10,
369
+ elevation: 5,
280
370
  },
281
- centerCluster: {
371
+ psShare: {
372
+ width: 12,
373
+ height: 25,
374
+ position: "absolute",
375
+ backgroundColor: "#95a5a6",
376
+ marginLeft: 125,
377
+ marginTop: 85,
378
+ borderRadius: 5,
379
+ zIndex: 10,
380
+ shadowColor: "#000",
381
+ shadowOffset: { width: 0, height: 2 },
382
+ shadowOpacity: 0.3,
383
+ shadowRadius: 5,
384
+ elevation: 3,
385
+ },
386
+ psOptions: {
387
+ width: 12,
388
+ height: 25,
389
+ position: "absolute",
390
+ backgroundColor: "#95a5a6",
391
+ marginLeft: 305,
392
+ marginTop: 85,
393
+ borderRadius: 5,
394
+ zIndex: 10,
395
+ shadowColor: "#000",
396
+ shadowOffset: { width: 0, height: 2 },
397
+ shadowOpacity: 0.3,
398
+ shadowRadius: 5,
399
+ elevation: 3,
400
+ },
401
+ psLeftHand: {
402
+ width: 120,
403
+ height: 260,
404
+ backgroundColor: "#e0e0e0",
405
+ position: "absolute",
406
+ left: 0,
407
+ top: 95,
408
+ transform: [{ rotate: "11deg" }],
409
+ borderTopLeftRadius: 30,
410
+ borderTopRightRadius: 50,
411
+ borderBottomRightRadius: 50,
412
+ borderBottomLeftRadius: 50,
413
+ shadowColor: "#000",
414
+ shadowOffset: { width: -5, height: 5 },
415
+ shadowOpacity: 0.4,
416
+ shadowRadius: 15,
417
+ elevation: 10,
418
+ zIndex: 1,
419
+ paddingTop: 5,
420
+ paddingLeft: 5,
421
+ },
422
+ psRightHand: {
423
+ width: 120,
424
+ height: 260,
425
+ backgroundColor: "#e0e0e0",
426
+ position: "absolute",
427
+ left: 330,
428
+ top: 95,
429
+ transform: [{ rotate: "-11deg" }],
430
+ borderTopLeftRadius: 30,
431
+ borderTopRightRadius: 30,
432
+ borderBottomRightRadius: 50,
433
+ borderBottomLeftRadius: 50,
434
+ shadowColor: "#000",
435
+ shadowOffset: { width: 5, height: 5 },
436
+ shadowOpacity: 0.4,
437
+ shadowRadius: 15,
438
+ elevation: 10,
439
+ zIndex: 1,
440
+ paddingTop: 2,
282
441
  alignItems: "center",
283
- gap: 6,
284
442
  },
285
- smallKey: {
286
- paddingVertical: 6,
287
- paddingHorizontal: 10,
288
- borderRadius: 8,
289
- backgroundColor: "#e2e8f0",
443
+ psLeftPad: {
444
+ backgroundColor: "#c0c0c0",
445
+ width: 112,
446
+ height: 112,
447
+ borderRadius: 56,
448
+ marginTop: 5,
449
+ marginLeft: 10,
290
450
  borderWidth: 1,
291
- borderColor: "#cbd5e1",
451
+ borderColor: "#b0b0b0",
452
+ position: "relative",
292
453
  },
293
- faceCluster: {
294
- alignItems: "center",
295
- gap: 4,
454
+ psArrowUp: {
455
+ width: 22,
456
+ height: 24,
457
+ backgroundColor: "#333",
458
+ position: "absolute",
459
+ top: 18,
460
+ left: 40,
461
+ transform: [{ rotate: "-11deg" }],
462
+ shadowColor: "#000",
463
+ shadowOffset: { width: 0, height: 4 },
464
+ shadowOpacity: 0.5,
465
+ shadowRadius: 5,
466
+ elevation: 5,
296
467
  },
297
- faceRow: {
298
- flexDirection: "row",
299
- gap: 8,
300
- justifyContent: "center",
468
+ psArrowDown: {
469
+ width: 22,
470
+ height: 24,
471
+ backgroundColor: "#333",
472
+ position: "absolute",
473
+ top: 70,
474
+ left: 50,
475
+ transform: [{ rotate: "-11deg" }],
476
+ shadowColor: "#000",
477
+ shadowOffset: { width: 0, height: 4 },
478
+ shadowOpacity: 0.5,
479
+ shadowRadius: 5,
480
+ elevation: 5,
301
481
  },
302
- faceButton: {
303
- width: 42,
304
- height: 42,
305
- borderRadius: 21,
306
- backgroundColor: "#e0f2fe",
482
+ psArrowRight: {
483
+ width: 24,
484
+ height: 22,
485
+ backgroundColor: "#333",
486
+ position: "absolute",
487
+ top: 40,
488
+ left: 72,
489
+ transform: [{ rotate: "-11deg" }],
490
+ shadowColor: "#000",
491
+ shadowOffset: { width: 0, height: 4 },
492
+ shadowOpacity: 0.5,
493
+ shadowRadius: 5,
494
+ elevation: 5,
495
+ },
496
+ psArrowLeft: {
497
+ width: 24,
498
+ height: 22,
499
+ backgroundColor: "#333",
500
+ position: "absolute",
501
+ top: 50,
502
+ left: 17,
503
+ transform: [{ rotate: "-11deg" }],
504
+ shadowColor: "#000",
505
+ shadowOffset: { width: 0, height: 4 },
506
+ shadowOpacity: 0.5,
507
+ shadowRadius: 5,
508
+ elevation: 5,
509
+ },
510
+ psRightPad: {
511
+ backgroundColor: "#c0c0c0",
512
+ width: 112,
513
+ height: 112,
514
+ borderRadius: 56,
515
+ marginTop: 2,
307
516
  borderWidth: 1,
308
- borderColor: "#bae6fd",
517
+ borderColor: "#b0b0b0",
518
+ position: "relative",
519
+ },
520
+ psTriangle: {
521
+ width: 30,
522
+ height: 30,
523
+ position: "absolute",
524
+ borderRadius: 15,
525
+ marginTop: 7,
526
+ marginLeft: 49,
527
+ backgroundColor: "#e0e0e0",
528
+ shadowColor: "#000",
529
+ shadowOffset: { width: 2, height: 2 },
530
+ shadowOpacity: 0.3,
531
+ shadowRadius: 5,
532
+ elevation: 3,
533
+ },
534
+ psTriangleBas: {
535
+ width: 22,
536
+ height: 22,
537
+ position: "absolute",
538
+ marginTop: 27,
539
+ marginLeft: 50,
540
+ transform: [{ rotate: "11deg" }],
541
+ borderTopWidth: 3,
542
+ borderTopColor: "#00c081",
543
+ },
544
+ psTriangleDroit: {
545
+ width: 20,
546
+ height: 20,
547
+ position: "absolute",
548
+ marginTop: 14,
549
+ marginLeft: 51,
550
+ transform: [{ rotate: "67deg" }],
551
+ borderTopWidth: 3,
552
+ borderTopColor: "#00c081",
553
+ },
554
+ psTriangleGauche: {
555
+ width: 20,
556
+ height: 20,
557
+ position: "absolute",
558
+ marginTop: 15,
559
+ marginLeft: 56,
560
+ transform: [{ rotate: "-46deg" }],
561
+ borderTopWidth: 3,
562
+ borderTopColor: "#00c081",
563
+ },
564
+ psCroix: {
565
+ width: 30,
566
+ height: 30,
567
+ position: "absolute",
568
+ borderRadius: 15,
569
+ marginTop: 70,
570
+ marginLeft: 36,
571
+ backgroundColor: "#e0e0e0",
572
+ shadowColor: "#000",
573
+ shadowOffset: { width: 2, height: 2 },
574
+ shadowOpacity: 0.3,
575
+ shadowRadius: 5,
576
+ elevation: 3,
577
+ },
578
+ psCroixBleue: {
579
+ width: 24,
580
+ height: 24,
581
+ transform: [{ rotate: "53deg" }],
582
+ borderRightWidth: 3,
583
+ borderRightColor: "#0a86e5",
584
+ marginTop: -7,
585
+ marginLeft: -4,
586
+ },
587
+ psCroixBleue2: {
588
+ width: 24,
589
+ height: 24,
590
+ transform: [{ rotate: "-32deg" }],
591
+ borderRightWidth: 3,
592
+ borderRightColor: "#0a86e5",
593
+ marginTop: -9,
594
+ marginLeft: -6,
595
+ },
596
+ psCarre: {
597
+ width: 30,
598
+ height: 30,
599
+ position: "absolute",
600
+ borderRadius: 15,
601
+ marginTop: 32,
602
+ marginLeft: 10,
603
+ backgroundColor: "#e0e0e0",
604
+ shadowColor: "#000",
605
+ shadowOffset: { width: 2, height: 2 },
606
+ shadowOpacity: 0.3,
607
+ shadowRadius: 5,
608
+ elevation: 3,
609
+ },
610
+ psCarreRose: {
611
+ width: 17,
612
+ height: 17,
613
+ marginTop: 6,
614
+ marginLeft: 6,
615
+ transform: [{ rotate: "11deg" }],
616
+ borderWidth: 3,
617
+ borderColor: "#e95ce9",
618
+ },
619
+ psRond: {
620
+ width: 30,
621
+ height: 30,
622
+ position: "absolute",
623
+ borderRadius: 15,
624
+ marginTop: 44,
625
+ marginLeft: 74,
626
+ backgroundColor: "#e0e0e0",
627
+ shadowColor: "#000",
628
+ shadowOffset: { width: 2, height: 2 },
629
+ shadowOpacity: 0.3,
630
+ shadowRadius: 5,
631
+ elevation: 3,
632
+ },
633
+ psRondRouge: {
634
+ width: 16,
635
+ height: 16,
636
+ borderRadius: 8,
637
+ transform: [{ rotate: "11deg" }],
638
+ borderWidth: 3,
639
+ borderColor: "#ff3746",
640
+ marginTop: 7,
641
+ marginLeft: 7,
642
+ },
643
+ psRollLeft: {
644
+ width: 60,
645
+ height: 60,
646
+ backgroundColor: "#333",
647
+ position: "absolute",
648
+ borderRadius: 40,
649
+ left: -180,
650
+ top: 40,
651
+ zIndex: 5,
652
+ shadowColor: "#000",
653
+ shadowOffset: { width: 0, height: 5 },
654
+ shadowOpacity: 0.5,
655
+ shadowRadius: 10,
656
+ elevation: 6,
309
657
  alignItems: "center",
310
658
  justifyContent: "center",
311
659
  },
312
- faceText: {
313
- color: "#0f172a",
314
- fontWeight: "800",
315
- },
316
- faceActive: {
317
- backgroundColor: "#2563eb",
318
- borderColor: "#1d4ed8",
319
- shadowColor: "#2563eb",
320
- shadowOpacity: 0.6,
321
- shadowRadius: 6,
322
- },
323
- stickZone: {
324
- width: 96,
660
+ psRollRight: {
661
+ width: 60,
662
+ height: 60,
663
+ backgroundColor: "#333",
664
+ position: "absolute",
665
+ borderRadius: 40,
666
+ left: -73,
667
+ top: 62,
668
+ zIndex: 5,
669
+ shadowColor: "#000",
670
+ shadowOffset: { width: 0, height: 5 },
671
+ shadowOpacity: 0.5,
672
+ shadowRadius: 10,
673
+ elevation: 6,
325
674
  alignItems: "center",
326
- gap: 6,
675
+ justifyContent: "center",
327
676
  },
328
- stickRing: {
329
- width: 84,
330
- height: 84,
331
- borderRadius: 42,
677
+ psRollIn: {
678
+ width: 40,
679
+ height: 40,
680
+ backgroundColor: "#222",
681
+ position: "absolute",
682
+ borderRadius: 30,
683
+ left: 10,
684
+ top: 10,
332
685
  borderWidth: 1,
333
- borderColor: "#cbd5e1",
334
- backgroundColor: "#f8fafc",
686
+ borderColor: "#000",
335
687
  alignItems: "center",
336
688
  justifyContent: "center",
337
689
  },
338
- stick: {
339
- width: 38,
340
- height: 38,
341
- borderRadius: 19,
342
- backgroundColor: "#e2e8f0",
343
- borderWidth: 2,
344
- borderColor: "#cbd5e1",
690
+ psRollInIn: {
691
+ width: 22,
692
+ height: 22,
693
+ backgroundColor: "#6d6d6d",
694
+ position: "absolute",
695
+ borderRadius: 22.5,
696
+ left: 8,
697
+ top: 8,
698
+ },
699
+ psPsButton: {
700
+ width: 23,
701
+ height: 23,
702
+ backgroundColor: "#333",
703
+ position: "absolute",
704
+ borderRadius: 11.5,
705
+ left: -110,
706
+ top: 85,
707
+ zIndex: 10,
708
+ borderWidth: 1,
709
+ borderColor: "#555",
710
+ shadowColor: "#000",
711
+ shadowOffset: { width: 0, height: 0 },
712
+ shadowOpacity: 0.5,
713
+ shadowRadius: 7,
714
+ elevation: 4,
345
715
  },
346
- bottomRow: {
347
- flexDirection: "row",
348
- justifyContent: "space-between",
349
- alignItems: "center",
350
- gap: 12,
716
+ psActiveButton: {
717
+ backgroundColor: "#2563eb",
718
+ borderColor: "#1e3a8a",
719
+ shadowColor: "#2563eb",
720
+ shadowOpacity: 0.4,
721
+ shadowRadius: 6,
351
722
  },
352
- dpad: {
353
- width: 120,
354
- height: 120,
355
- alignItems: "center",
356
- justifyContent: "center",
357
- position: "relative",
723
+ psTriangleLineActive: {
724
+ borderTopColor: "#00f5a8",
358
725
  },
359
- dpadKey: {
360
- position: "absolute",
361
- backgroundColor: "#e2e8f0",
362
- borderColor: "#cbd5e1",
363
- borderWidth: 1,
364
- borderRadius: 6,
726
+ psPinkActive: {
727
+ borderColor: "#d946ef",
365
728
  },
366
- dpadVertical: {
367
- width: 28,
368
- height: 46,
729
+ psRedActive: {
730
+ borderColor: "#fb7185",
369
731
  },
370
- dpadHorizontal: {
371
- width: 46,
372
- height: 28,
732
+ psCrossLineActive: {
733
+ borderRightColor: "#2563eb",
373
734
  },
374
- active: {
375
- borderColor: "#2563eb",
376
- backgroundColor: "#2563eb",
735
+ psStickPressed: {
736
+ shadowOpacity: 0.65,
737
+ shadowRadius: 12,
738
+ },
739
+ psStickInnerPressed: {
740
+ borderColor: "#1e3a8a",
377
741
  shadowColor: "#2563eb",
378
- shadowOpacity: 0.5,
379
- shadowRadius: 4,
742
+ shadowOpacity: 0.45,
743
+ shadowRadius: 10,
380
744
  },
381
- dpadUp: { top: 6, left: 46 },
382
- dpadDown: { bottom: 6, left: 46 },
383
- dpadLeft: { left: 6, top: 46 },
384
- dpadRight: { right: 6, top: 46 },
385
- axesGrid: {
386
- flexDirection: "row",
387
- flexWrap: "wrap",
388
- gap: 8,
745
+ psStickCenterPressed: {
746
+ borderColor: "#93c5fd",
747
+ backgroundColor: "#dbeafe",
389
748
  },
390
749
  });
@@ -1,5 +1,5 @@
1
- import { type JSX } from 'react';
2
- import type { AxisEvent, ButtonEvent, DpadEvent, GamepadButtonName, StickAxisName, StatusEvent } from './types';
1
+ import { type JSX } from "react";
2
+ import type { AxisEvent, ButtonEvent, DpadEvent, GamepadButtonName, StickAxisName, StatusEvent } from "./types";
3
3
  type Options = {
4
4
  enabled?: boolean;
5
5
  axisThreshold?: number;
@@ -11,6 +11,7 @@ type Options = {
11
11
  type Return = {
12
12
  pressedButtons: Set<GamepadButtonName>;
13
13
  axes: Partial<Record<StickAxisName, number>>;
14
+ buttonValues: Partial<Record<GamepadButtonName, number>>;
14
15
  isPressed: (key: GamepadButtonName) => boolean;
15
16
  bridge: JSX.Element | null;
16
17
  };
@@ -9,6 +9,7 @@ const react_1 = require("react");
9
9
  const GamepadBridge_1 = __importDefault(require("./GamepadBridge"));
10
10
  function useGamepad({ enabled = true, axisThreshold = 0.15, onButton, onAxis, onDpad, onStatus, } = {}) {
11
11
  const [pressedButtons, setPressedButtons] = (0, react_1.useState)(new Set());
12
+ const [buttonValues, setButtonValues] = (0, react_1.useState)({});
12
13
  const pressedRef = (0, react_1.useRef)(new Set());
13
14
  const [axes, setAxes] = (0, react_1.useState)({
14
15
  leftX: 0,
@@ -29,6 +30,10 @@ function useGamepad({ enabled = true, axisThreshold = 0.15, onButton, onAxis, on
29
30
  }
30
31
  pressedRef.current = next;
31
32
  setPressedButtons(next);
33
+ setButtonValues((prev) => ({
34
+ ...prev,
35
+ [event.button]: event.value,
36
+ }));
32
37
  onButton === null || onButton === void 0 ? void 0 : onButton(event);
33
38
  }, [onButton]);
34
39
  const handleAxis = (0, react_1.useCallback)((event) => {
@@ -45,9 +50,10 @@ function useGamepad({ enabled = true, axisThreshold = 0.15, onButton, onAxis, on
45
50
  }
46
51
  if (!enabled) {
47
52
  setAxes({ leftX: 0, leftY: 0, rightX: 0, rightY: 0 });
53
+ setButtonValues({});
48
54
  }
49
55
  }, [enabled]);
50
56
  const bridge = (0, react_1.useMemo)(() => ((0, jsx_runtime_1.jsx)(GamepadBridge_1.default, { enabled: enabled, axisThreshold: axisThreshold, onDpad: handleDpad, onButton: handleButton, onAxis: handleAxis, onStatus: onStatus })), [enabled, axisThreshold, handleAxis, handleButton, handleDpad, onStatus]);
51
57
  const isPressed = (0, react_1.useCallback)((key) => pressedRef.current.has(key), []);
52
- return { pressedButtons, axes, isPressed, bridge };
58
+ return { pressedButtons, axes, buttonValues, isPressed, bridge };
53
59
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-earl-gamepad",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "React Native gamepad bridge via WebView (buttons, sticks, d-pad, status).",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",