@renegades/react-native-tickle 0.1.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.
Files changed (79) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +352 -0
  3. package/Tickle.podspec +29 -0
  4. package/android/CMakeLists.txt +24 -0
  5. package/android/build.gradle +126 -0
  6. package/android/gradle.properties +5 -0
  7. package/android/src/main/AndroidManifest.xml +2 -0
  8. package/android/src/main/cpp/cpp-adapter.cpp +6 -0
  9. package/android/src/main/java/com/margelo/nitro/tickle/Tickle.kt +71 -0
  10. package/android/src/main/java/com/margelo/nitro/tickle/TicklePackage.kt +22 -0
  11. package/ios/Tickle.swift +185 -0
  12. package/ios/TickleUtils.swift +404 -0
  13. package/lib/module/Tickle.nitro.js +4 -0
  14. package/lib/module/Tickle.nitro.js.map +1 -0
  15. package/lib/module/index.js +254 -0
  16. package/lib/module/index.js.map +1 -0
  17. package/lib/module/package.json +1 -0
  18. package/lib/typescript/package.json +1 -0
  19. package/lib/typescript/src/Tickle.nitro.d.ts +63 -0
  20. package/lib/typescript/src/Tickle.nitro.d.ts.map +1 -0
  21. package/lib/typescript/src/index.d.ts +148 -0
  22. package/lib/typescript/src/index.d.ts.map +1 -0
  23. package/nitro.json +17 -0
  24. package/nitrogen/generated/android/c++/JHapticCurve.hpp +87 -0
  25. package/nitrogen/generated/android/c++/JHapticCurveControlPoint.hpp +61 -0
  26. package/nitrogen/generated/android/c++/JHapticEvent.hpp +94 -0
  27. package/nitrogen/generated/android/c++/JHapticEventParameter.hpp +62 -0
  28. package/nitrogen/generated/android/c++/JHapticEventType.hpp +58 -0
  29. package/nitrogen/generated/android/c++/JHapticImpactStyle.hpp +67 -0
  30. package/nitrogen/generated/android/c++/JHapticNotificationType.hpp +61 -0
  31. package/nitrogen/generated/android/c++/JHapticParameterType.hpp +58 -0
  32. package/nitrogen/generated/android/c++/JHybridTickleSpec.cpp +162 -0
  33. package/nitrogen/generated/android/c++/JHybridTickleSpec.hpp +79 -0
  34. package/nitrogen/generated/android/kotlin/com/margelo/nitro/tickle/HapticCurve.kt +44 -0
  35. package/nitrogen/generated/android/kotlin/com/margelo/nitro/tickle/HapticCurveControlPoint.kt +41 -0
  36. package/nitrogen/generated/android/kotlin/com/margelo/nitro/tickle/HapticEvent.kt +47 -0
  37. package/nitrogen/generated/android/kotlin/com/margelo/nitro/tickle/HapticEventParameter.kt +41 -0
  38. package/nitrogen/generated/android/kotlin/com/margelo/nitro/tickle/HapticEventType.kt +23 -0
  39. package/nitrogen/generated/android/kotlin/com/margelo/nitro/tickle/HapticImpactStyle.kt +26 -0
  40. package/nitrogen/generated/android/kotlin/com/margelo/nitro/tickle/HapticNotificationType.kt +24 -0
  41. package/nitrogen/generated/android/kotlin/com/margelo/nitro/tickle/HapticParameterType.kt +23 -0
  42. package/nitrogen/generated/android/kotlin/com/margelo/nitro/tickle/HybridTickleSpec.kt +109 -0
  43. package/nitrogen/generated/android/kotlin/com/margelo/nitro/tickle/tickleOnLoad.kt +35 -0
  44. package/nitrogen/generated/android/tickle+autolinking.cmake +81 -0
  45. package/nitrogen/generated/android/tickle+autolinking.gradle +27 -0
  46. package/nitrogen/generated/android/tickleOnLoad.cpp +44 -0
  47. package/nitrogen/generated/android/tickleOnLoad.hpp +25 -0
  48. package/nitrogen/generated/ios/Tickle+autolinking.rb +60 -0
  49. package/nitrogen/generated/ios/Tickle-Swift-Cxx-Bridge.cpp +33 -0
  50. package/nitrogen/generated/ios/Tickle-Swift-Cxx-Bridge.hpp +139 -0
  51. package/nitrogen/generated/ios/Tickle-Swift-Cxx-Umbrella.hpp +70 -0
  52. package/nitrogen/generated/ios/TickleAutolinking.mm +33 -0
  53. package/nitrogen/generated/ios/TickleAutolinking.swift +25 -0
  54. package/nitrogen/generated/ios/c++/HybridTickleSpecSwift.cpp +11 -0
  55. package/nitrogen/generated/ios/c++/HybridTickleSpecSwift.hpp +185 -0
  56. package/nitrogen/generated/ios/swift/HapticCurve.swift +46 -0
  57. package/nitrogen/generated/ios/swift/HapticCurveControlPoint.swift +35 -0
  58. package/nitrogen/generated/ios/swift/HapticEvent.swift +57 -0
  59. package/nitrogen/generated/ios/swift/HapticEventParameter.swift +35 -0
  60. package/nitrogen/generated/ios/swift/HapticEventType.swift +40 -0
  61. package/nitrogen/generated/ios/swift/HapticImpactStyle.swift +52 -0
  62. package/nitrogen/generated/ios/swift/HapticNotificationType.swift +44 -0
  63. package/nitrogen/generated/ios/swift/HapticParameterType.swift +40 -0
  64. package/nitrogen/generated/ios/swift/HybridTickleSpec.swift +69 -0
  65. package/nitrogen/generated/ios/swift/HybridTickleSpec_cxx.swift +282 -0
  66. package/nitrogen/generated/shared/c++/HapticCurve.hpp +96 -0
  67. package/nitrogen/generated/shared/c++/HapticCurveControlPoint.hpp +87 -0
  68. package/nitrogen/generated/shared/c++/HapticEvent.hpp +101 -0
  69. package/nitrogen/generated/shared/c++/HapticEventParameter.hpp +88 -0
  70. package/nitrogen/generated/shared/c++/HapticEventType.hpp +76 -0
  71. package/nitrogen/generated/shared/c++/HapticImpactStyle.hpp +88 -0
  72. package/nitrogen/generated/shared/c++/HapticNotificationType.hpp +80 -0
  73. package/nitrogen/generated/shared/c++/HapticParameterType.hpp +76 -0
  74. package/nitrogen/generated/shared/c++/HybridTickleSpec.cpp +34 -0
  75. package/nitrogen/generated/shared/c++/HybridTickleSpec.hpp +87 -0
  76. package/package.json +179 -0
  77. package/react-native.config.js +8 -0
  78. package/src/Tickle.nitro.ts +84 -0
  79. package/src/index.tsx +306 -0
@@ -0,0 +1,87 @@
1
+ ///
2
+ /// HybridTickleSpec.hpp
3
+ /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
4
+ /// https://github.com/mrousavy/nitro
5
+ /// Copyright © Marc Rousavy @ Margelo
6
+ ///
7
+
8
+ #pragma once
9
+
10
+ #if __has_include(<NitroModules/HybridObject.hpp>)
11
+ #include <NitroModules/HybridObject.hpp>
12
+ #else
13
+ #error NitroModules cannot be found! Are you sure you installed NitroModules properly?
14
+ #endif
15
+
16
+ // Forward declaration of `HapticEvent` to properly resolve imports.
17
+ namespace margelo::nitro::tickle { struct HapticEvent; }
18
+ // Forward declaration of `HapticCurve` to properly resolve imports.
19
+ namespace margelo::nitro::tickle { struct HapticCurve; }
20
+ // Forward declaration of `HapticImpactStyle` to properly resolve imports.
21
+ namespace margelo::nitro::tickle { enum class HapticImpactStyle; }
22
+ // Forward declaration of `HapticNotificationType` to properly resolve imports.
23
+ namespace margelo::nitro::tickle { enum class HapticNotificationType; }
24
+
25
+ #include "HapticEvent.hpp"
26
+ #include <vector>
27
+ #include "HapticCurve.hpp"
28
+ #include <string>
29
+ #include "HapticImpactStyle.hpp"
30
+ #include "HapticNotificationType.hpp"
31
+
32
+ namespace margelo::nitro::tickle {
33
+
34
+ using namespace margelo::nitro;
35
+
36
+ /**
37
+ * An abstract base class for `Tickle`
38
+ * Inherit this class to create instances of `HybridTickleSpec` in C++.
39
+ * You must explicitly call `HybridObject`'s constructor yourself, because it is virtual.
40
+ * @example
41
+ * ```cpp
42
+ * class HybridTickle: public HybridTickleSpec {
43
+ * public:
44
+ * HybridTickle(...): HybridObject(TAG) { ... }
45
+ * // ...
46
+ * };
47
+ * ```
48
+ */
49
+ class HybridTickleSpec: public virtual HybridObject {
50
+ public:
51
+ // Constructor
52
+ explicit HybridTickleSpec(): HybridObject(TAG) { }
53
+
54
+ // Destructor
55
+ ~HybridTickleSpec() override = default;
56
+
57
+ public:
58
+ // Properties
59
+
60
+
61
+ public:
62
+ // Methods
63
+ virtual void startHaptic(const std::vector<HapticEvent>& events, const std::vector<HapticCurve>& curves) = 0;
64
+ virtual void stopAllHaptics() = 0;
65
+ virtual void initializeEngine() = 0;
66
+ virtual void destroyEngine() = 0;
67
+ virtual void createContinuousPlayer(const std::string& playerId, double initialIntensity, double initialSharpness) = 0;
68
+ virtual void startContinuousPlayer(const std::string& playerId) = 0;
69
+ virtual void updateContinuousPlayer(const std::string& playerId, double intensityControl, double sharpnessControl) = 0;
70
+ virtual void stopContinuousPlayer(const std::string& playerId) = 0;
71
+ virtual void destroyContinuousPlayer(const std::string& playerId) = 0;
72
+ virtual void setHapticsEnabled(bool enabled) = 0;
73
+ virtual bool getHapticsEnabled() = 0;
74
+ virtual void triggerImpact(HapticImpactStyle style) = 0;
75
+ virtual void triggerNotification(HapticNotificationType type) = 0;
76
+ virtual void triggerSelection() = 0;
77
+
78
+ protected:
79
+ // Hybrid Setup
80
+ void loadHybridMethods() override;
81
+
82
+ protected:
83
+ // Tag for logging
84
+ static constexpr auto TAG = "Tickle";
85
+ };
86
+
87
+ } // namespace margelo::nitro::tickle
package/package.json ADDED
@@ -0,0 +1,179 @@
1
+ {
2
+ "name": "@renegades/react-native-tickle",
3
+ "version": "0.1.0",
4
+ "description": "Build and fine-tune haptic patterns with real-time previews",
5
+ "main": "./lib/module/index.js",
6
+ "types": "./lib/typescript/src/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "source": "./src/index.tsx",
10
+ "types": "./lib/typescript/src/index.d.ts",
11
+ "default": "./lib/module/index.js"
12
+ },
13
+ "./package.json": "./package.json"
14
+ },
15
+ "files": [
16
+ "src",
17
+ "lib",
18
+ "android",
19
+ "ios",
20
+ "cpp",
21
+ "nitrogen",
22
+ "nitro.json",
23
+ "*.podspec",
24
+ "react-native.config.js",
25
+ "!ios/build",
26
+ "!android/build",
27
+ "!android/gradle",
28
+ "!android/gradlew",
29
+ "!android/gradlew.bat",
30
+ "!android/local.properties",
31
+ "!**/__tests__",
32
+ "!**/__fixtures__",
33
+ "!**/__mocks__",
34
+ "!**/.*"
35
+ ],
36
+ "scripts": {
37
+ "postinstall": "bash scripts/postinstall.sh",
38
+ "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
39
+ "prepare": "bob build",
40
+ "nitrogen": "nitrogen",
41
+ "typecheck": "tsc",
42
+ "lint": "eslint \"**/*.{js,ts,tsx}\"",
43
+ "test": "jest",
44
+ "release": "release-it --only-version"
45
+ },
46
+ "keywords": [
47
+ "react-native",
48
+ "ios",
49
+ "android"
50
+ ],
51
+ "repository": {
52
+ "type": "git",
53
+ "url": "git+https://github.com/Renegades-Studio/react-native-tickle.git"
54
+ },
55
+ "author": "Renegades <info@renegades.xyz> (https://renegades.xyz)",
56
+ "license": "MIT",
57
+ "bugs": {
58
+ "url": "https://github.com/Renegades-Studio/react-native-tickle/issues"
59
+ },
60
+ "homepage": "https://github.com/Renegades-Studio/react-native-tickle#readme",
61
+ "publishConfig": {
62
+ "registry": "https://registry.npmjs.org/"
63
+ },
64
+ "devDependencies": {
65
+ "@commitlint/config-conventional": "^19.8.1",
66
+ "@eslint/compat": "^1.3.2",
67
+ "@eslint/eslintrc": "^3.3.1",
68
+ "@eslint/js": "^9.35.0",
69
+ "@react-native/babel-preset": "0.83.0",
70
+ "@react-native/eslint-config": "0.83.0",
71
+ "@react-native/eslint-plugin": "^0.83.1",
72
+ "@release-it/conventional-changelog": "^10.0.1",
73
+ "@types/jest": "^29.5.14",
74
+ "@types/react": "~19.1.10",
75
+ "@typescript-eslint/eslint-plugin": "^8.52.0",
76
+ "commitlint": "^19.8.1",
77
+ "del-cli": "^6.0.0",
78
+ "eslint": "^9.35.0",
79
+ "eslint-config-prettier": "^10.1.8",
80
+ "eslint-plugin-eslint-comments": "^3.2.0",
81
+ "eslint-plugin-ft-flow": "^3.0.11",
82
+ "eslint-plugin-jest": "^29.12.1",
83
+ "eslint-plugin-prettier": "^5.5.4",
84
+ "eslint-plugin-react": "^7.37.5",
85
+ "eslint-plugin-react-hooks": "^7.0.1",
86
+ "eslint-plugin-react-native": "^5.0.0",
87
+ "jest": "^29.7.0",
88
+ "lefthook": "^2.0.3",
89
+ "nitrogen": "^0.32.1",
90
+ "prettier": "^2.8.8",
91
+ "react": "19.1.0",
92
+ "react-native": "0.81.5",
93
+ "react-native-builder-bob": "^0.40.17",
94
+ "react-native-nitro-modules": "0.32.1",
95
+ "release-it": "^19.0.4",
96
+ "turbo": "^2.5.6",
97
+ "typescript": "^5.9.2"
98
+ },
99
+ "peerDependencies": {
100
+ "react": "*",
101
+ "react-native": "*",
102
+ "react-native-nitro-modules": "^0.32.0"
103
+ },
104
+ "packageManager": "bun@1.1.38",
105
+ "react-native-builder-bob": {
106
+ "source": "src",
107
+ "output": "lib",
108
+ "targets": [
109
+ [
110
+ "custom",
111
+ {
112
+ "script": "nitrogen",
113
+ "clean": "nitrogen/"
114
+ }
115
+ ],
116
+ [
117
+ "module",
118
+ {
119
+ "esm": true
120
+ }
121
+ ],
122
+ [
123
+ "typescript",
124
+ {
125
+ "project": "tsconfig.build.json"
126
+ }
127
+ ]
128
+ ]
129
+ },
130
+ "prettier": {
131
+ "quoteProps": "consistent",
132
+ "singleQuote": true,
133
+ "tabWidth": 2,
134
+ "trailingComma": "es5",
135
+ "useTabs": false
136
+ },
137
+ "jest": {
138
+ "preset": "react-native",
139
+ "modulePathIgnorePatterns": [
140
+ "<rootDir>/example/node_modules",
141
+ "<rootDir>/lib/"
142
+ ]
143
+ },
144
+ "commitlint": {
145
+ "extends": [
146
+ "@commitlint/config-conventional"
147
+ ]
148
+ },
149
+ "release-it": {
150
+ "git": {
151
+ "commitMessage": "chore: release ${version}",
152
+ "tagName": "v${version}"
153
+ },
154
+ "npm": {
155
+ "publish": true
156
+ },
157
+ "github": {
158
+ "release": true
159
+ },
160
+ "plugins": {
161
+ "@release-it/conventional-changelog": {
162
+ "preset": {
163
+ "name": "angular"
164
+ }
165
+ }
166
+ }
167
+ },
168
+ "create-react-native-library": {
169
+ "type": "nitro-module",
170
+ "languages": "kotlin-swift",
171
+ "tools": [
172
+ "eslint",
173
+ "jest",
174
+ "lefthook",
175
+ "release-it"
176
+ ],
177
+ "version": "0.56.0"
178
+ }
179
+ }
@@ -0,0 +1,8 @@
1
+ module.exports = {
2
+ dependency: {
3
+ platforms: {
4
+ ios: {},
5
+ android: {},
6
+ },
7
+ },
8
+ };
@@ -0,0 +1,84 @@
1
+ import type { HybridObject } from 'react-native-nitro-modules';
2
+
3
+ export type HapticEventType = 'transient' | 'continuous';
4
+ export type HapticParameterType = 'intensity' | 'sharpness';
5
+
6
+ // MARK: - System Haptic Types
7
+
8
+ /**
9
+ * Feedback intensity styles for impact haptics.
10
+ * Maps to UIImpactFeedbackGenerator.FeedbackStyle on iOS.
11
+ */
12
+ export type HapticImpactStyle = 'rigid' | 'heavy' | 'medium' | 'light' | 'soft';
13
+
14
+ /**
15
+ * Notification feedback categories for alert-style haptics.
16
+ * Maps to UINotificationFeedbackGenerator.FeedbackType on iOS.
17
+ */
18
+ export type HapticNotificationType = 'error' | 'success' | 'warning';
19
+
20
+ export type HapticEventParameter = {
21
+ type: HapticParameterType;
22
+ value: number;
23
+ };
24
+ export type HapticCurveControlPoint = {
25
+ relativeTime: number;
26
+ value: number;
27
+ };
28
+
29
+ export type HapticEvent = {
30
+ type: HapticEventType;
31
+ parameters: HapticEventParameter[];
32
+ relativeTime: number;
33
+ duration?: number; // Optional for transient events
34
+ };
35
+
36
+ export type HapticCurve = {
37
+ type: HapticParameterType;
38
+ controlPoints: HapticCurveControlPoint[];
39
+ relativeTime: number;
40
+ };
41
+
42
+ export interface Tickle
43
+ extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> {
44
+ startHaptic(events: HapticEvent[], curves: HapticCurve[]): void;
45
+ stopAllHaptics(): void;
46
+ initializeEngine(): void;
47
+ destroyEngine(): void;
48
+
49
+ createContinuousPlayer(
50
+ playerId: string,
51
+ initialIntensity: number,
52
+ initialSharpness: number
53
+ ): void;
54
+ startContinuousPlayer(playerId: string): void;
55
+ updateContinuousPlayer(
56
+ playerId: string,
57
+ intensityControl: number,
58
+ sharpnessControl: number
59
+ ): void;
60
+ stopContinuousPlayer(playerId: string): void;
61
+ destroyContinuousPlayer(playerId: string): void;
62
+
63
+ setHapticsEnabled(enabled: boolean): void;
64
+ getHapticsEnabled(): boolean;
65
+
66
+ // MARK: - System Haptics (Predefined OS-level feedback)
67
+
68
+ /**
69
+ * Triggers an impact haptic with the specified style.
70
+ * Simulates a physical collision feedback.
71
+ */
72
+ triggerImpact(style: HapticImpactStyle): void;
73
+
74
+ /**
75
+ * Triggers a notification-style haptic for alerts and status updates.
76
+ */
77
+ triggerNotification(type: HapticNotificationType): void;
78
+
79
+ /**
80
+ * Triggers a selection change haptic.
81
+ * Best used for picker wheels, toggles, and selection changes.
82
+ */
83
+ triggerSelection(): void;
84
+ }
package/src/index.tsx ADDED
@@ -0,0 +1,306 @@
1
+ import { useEffect, useState, useCallback } from 'react';
2
+ import { AppState } from 'react-native';
3
+ import { NitroModules } from 'react-native-nitro-modules';
4
+ import type {
5
+ Tickle,
6
+ HapticCurve,
7
+ HapticEventParameter,
8
+ HapticParameterType,
9
+ HapticImpactStyle,
10
+ HapticNotificationType,
11
+ } from './Tickle.nitro';
12
+
13
+ export type TransientHapticEvent = {
14
+ type: 'transient';
15
+ parameters: HapticEventParameter[];
16
+ relativeTime: number;
17
+ };
18
+
19
+ export type ContinuousHapticEvent = {
20
+ type: 'continuous';
21
+ parameters: HapticEventParameter[];
22
+ relativeTime: number;
23
+ duration: number;
24
+ };
25
+
26
+ export type HapticEvent = TransientHapticEvent | ContinuousHapticEvent;
27
+
28
+ const TickleHybridObject = NitroModules.createHybridObject<Tickle>('Tickle');
29
+
30
+ const boxedTickle = NitroModules.box(TickleHybridObject);
31
+
32
+ export function startHaptic(
33
+ events: HapticEvent[],
34
+ curves: HapticCurve[]
35
+ ): void {
36
+ 'worklet';
37
+
38
+ return boxedTickle.unbox().startHaptic(events, curves);
39
+ }
40
+
41
+ export function stopAllHaptics(): void {
42
+ 'worklet';
43
+
44
+ return boxedTickle.unbox().stopAllHaptics();
45
+ }
46
+
47
+ export function initializeEngine(): void {
48
+ 'worklet';
49
+
50
+ return boxedTickle.unbox().initializeEngine();
51
+ }
52
+
53
+ export function destroyEngine(): void {
54
+ 'worklet';
55
+
56
+ return boxedTickle.unbox().destroyEngine();
57
+ }
58
+
59
+ export function createContinuousPlayer(
60
+ playerId: string,
61
+ initialIntensity: number,
62
+ initialSharpness: number
63
+ ): void {
64
+ 'worklet';
65
+
66
+ return boxedTickle
67
+ .unbox()
68
+ .createContinuousPlayer(playerId, initialIntensity, initialSharpness);
69
+ }
70
+
71
+ export function startContinuousPlayer(playerId: string): void {
72
+ 'worklet';
73
+
74
+ return boxedTickle.unbox().startContinuousPlayer(playerId);
75
+ }
76
+
77
+ export function updateContinuousPlayer(
78
+ playerId: string,
79
+ intensityControl: number,
80
+ sharpnessControl: number
81
+ ): void {
82
+ 'worklet';
83
+
84
+ return boxedTickle
85
+ .unbox()
86
+ .updateContinuousPlayer(playerId, intensityControl, sharpnessControl);
87
+ }
88
+
89
+ export function stopContinuousPlayer(playerId: string): void {
90
+ 'worklet';
91
+ return boxedTickle.unbox().stopContinuousPlayer(playerId);
92
+ }
93
+
94
+ export function destroyContinuousPlayer(playerId: string): void {
95
+ 'worklet';
96
+ return boxedTickle.unbox().destroyContinuousPlayer(playerId);
97
+ }
98
+
99
+ // MARK: - System Haptics (Predefined OS-level feedback)
100
+
101
+ /**
102
+ * Triggers an impact haptic with the specified style.
103
+ * Simulates a physical collision feedback.
104
+ *
105
+ * @param style - The intensity style of the impact ('rigid' | 'heavy' | 'medium' | 'light' | 'soft')
106
+ *
107
+ * @example
108
+ * ```ts
109
+ * // Light tap for subtle UI feedback
110
+ * triggerImpact('light');
111
+ *
112
+ * // Heavy impact for significant actions
113
+ * triggerImpact('heavy');
114
+ * ```
115
+ */
116
+ export function triggerImpact(style: HapticImpactStyle): void {
117
+ 'worklet';
118
+ return boxedTickle.unbox().triggerImpact(style);
119
+ }
120
+
121
+ /**
122
+ * Triggers a notification-style haptic for alerts and status updates.
123
+ * Use this for communicating outcomes of actions to the user.
124
+ *
125
+ * @param type - The notification type ('error' | 'success' | 'warning')
126
+ *
127
+ * @example
128
+ * ```ts
129
+ * // Success feedback after completing an action
130
+ * triggerNotification('success');
131
+ *
132
+ * // Error feedback when something goes wrong
133
+ * triggerNotification('error');
134
+ * ```
135
+ */
136
+ export function triggerNotification(type: HapticNotificationType): void {
137
+ 'worklet';
138
+ return boxedTickle.unbox().triggerNotification(type);
139
+ }
140
+
141
+ /**
142
+ * Triggers a selection change haptic.
143
+ * Best used for picker wheels, toggles, and selection changes.
144
+ *
145
+ * @example
146
+ * ```ts
147
+ * // When user scrolls through picker options
148
+ * triggerSelection();
149
+ * ```
150
+ */
151
+ export function triggerSelection(): void {
152
+ 'worklet';
153
+ return boxedTickle.unbox().triggerSelection();
154
+ }
155
+
156
+ // MARK: - Global Haptics Enable/Disable
157
+
158
+ /**
159
+ * Enable or disable haptics globally. This setting is persisted across app restarts.
160
+ * When disabled, all haptic functions become no-ops (no haptics will play).
161
+ * This does not affect engine initialization/destruction.
162
+ *
163
+ * @param enabled - Whether haptics should be enabled
164
+ */
165
+ export function setHapticsEnabled(enabled: boolean): void {
166
+ 'worklet';
167
+ return boxedTickle.unbox().setHapticsEnabled(enabled);
168
+ }
169
+
170
+ /**
171
+ * Get the current global haptics enabled state.
172
+ * Defaults to true if not previously set.
173
+ *
174
+ * @returns Whether haptics are currently enabled
175
+ */
176
+ export function getHapticsEnabled(): boolean {
177
+ 'worklet';
178
+ return boxedTickle.unbox().getHapticsEnabled();
179
+ }
180
+
181
+ /**
182
+ * Hook to manage the global haptics enabled state.
183
+ * Provides reactive state and a setter function.
184
+ *
185
+ * @returns [isEnabled, setEnabled] - Current state and setter function
186
+ *
187
+ * @example
188
+ * ```tsx
189
+ * function SettingsScreen() {
190
+ * const [hapticsEnabled, setHapticsEnabled] = useHapticsEnabled();
191
+ *
192
+ * return (
193
+ * <Switch
194
+ * value={hapticsEnabled}
195
+ * onValueChange={setHapticsEnabled}
196
+ * />
197
+ * );
198
+ * }
199
+ * ```
200
+ */
201
+ export function useHapticsEnabled(): [boolean, (enabled: boolean) => void] {
202
+ const [enabled, setEnabled] = useState(() => getHapticsEnabled());
203
+
204
+ const setHapticsEnabledState = useCallback((value: boolean) => {
205
+ setHapticsEnabled(value);
206
+ setEnabled(value);
207
+ }, []);
208
+
209
+ return [enabled, setHapticsEnabledState] as const;
210
+ }
211
+
212
+ /**
213
+ * Hook to manage a continuous haptic player lifecycle.
214
+ *
215
+ * @param playerId - A unique string key to identify this player (e.g., 'my-palette')
216
+ * @param initialIntensity - Initial intensity value (0.0 to 1.0)
217
+ * @param initialSharpness - Initial sharpness value (0.0 to 1.0)
218
+ *
219
+ * @returns Object with JS-bound methods for convenience:
220
+ * - `start()` - Start the continuous haptic
221
+ * - `stop()` - Stop the continuous haptic
222
+ * - `update(intensity, sharpness)` - Update haptic parameters
223
+ * - `playerId` - The player key (for use in worklets)
224
+ *
225
+ * @example
226
+ * ```tsx
227
+ * const PLAYER_KEY = 'my-palette';
228
+ *
229
+ * function MyComponent() {
230
+ * const { start, stop, update } = useContinuousPlayer(PLAYER_KEY, 1.0, 0.5);
231
+ *
232
+ * const gesture = Gesture.Pan()
233
+ * .onBegin(() => {
234
+ * start();
235
+ * })
236
+ * .onUpdate(() => {
237
+ * update(0.5, 0.5);
238
+ * })
239
+ * .onEnd(() => {
240
+ * stop();
241
+ * });
242
+ * }
243
+ * ```
244
+ */
245
+ export function useContinuousPlayer(
246
+ playerId: string,
247
+ initialIntensity: number = 1.0,
248
+ initialSharpness: number = 0.5
249
+ ) {
250
+ useEffect(() => {
251
+ createContinuousPlayer(playerId, initialIntensity, initialSharpness);
252
+
253
+ return () => {
254
+ destroyContinuousPlayer(playerId);
255
+ };
256
+ }, [playerId, initialIntensity, initialSharpness]);
257
+
258
+ return {
259
+ start: () => {
260
+ 'worklet';
261
+ startContinuousPlayer(playerId);
262
+ },
263
+ stop: () => {
264
+ 'worklet';
265
+ stopContinuousPlayer(playerId);
266
+ },
267
+ update: (intensity: number, sharpness: number) => {
268
+ 'worklet';
269
+ return updateContinuousPlayer(playerId, intensity, sharpness);
270
+ },
271
+ playerId,
272
+ };
273
+ }
274
+
275
+ export function useHapticEngine() {
276
+ useEffect(() => {
277
+ initializeEngine();
278
+
279
+ const off = AppState.addEventListener('change', (state) => {
280
+ if (state === 'active') {
281
+ initializeEngine();
282
+ } else if (state === 'background') {
283
+ destroyEngine();
284
+ }
285
+ });
286
+
287
+ return () => {
288
+ off.remove();
289
+ };
290
+ }, []);
291
+ }
292
+
293
+ export function HapticProvider({ children }: { children: React.ReactNode }) {
294
+ useHapticEngine();
295
+
296
+ return <>{children}</>;
297
+ }
298
+
299
+ export { TickleHybridObject };
300
+ export type {
301
+ HapticCurve,
302
+ HapticEventParameter,
303
+ HapticParameterType,
304
+ HapticImpactStyle,
305
+ HapticNotificationType,
306
+ };