@symbiote-native/haptics 0.0.1 → 0.2.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 (42) hide show
  1. package/README.md +130 -21
  2. package/build/angular/index.d.ts +1 -0
  3. package/build/angular/index.js +4 -0
  4. package/build/core/haptics.d.ts +24 -0
  5. package/build/core/haptics.js +50 -0
  6. package/build/core/index.d.ts +2 -0
  7. package/build/core/index.js +2 -0
  8. package/build/core/native-module.d.ts +8 -0
  9. package/build/core/native-module.js +3 -0
  10. package/build/core/types.d.ts +86 -0
  11. package/build/core/types.js +89 -0
  12. package/build/react/index.d.ts +1 -0
  13. package/build/react/index.js +5 -0
  14. package/build/svelte/index.d.ts +1 -0
  15. package/build/svelte/index.js +6 -0
  16. package/build/vue/index.d.ts +1 -0
  17. package/build/vue/index.js +4 -0
  18. package/build-ngc/angular/index.d.ts +1 -0
  19. package/build-ngc/angular/index.js +5 -0
  20. package/build-ngc/angular/index.js.map +1 -0
  21. package/build-ngc/core/haptics.d.ts +24 -0
  22. package/build-ngc/core/haptics.js +51 -0
  23. package/build-ngc/core/haptics.js.map +1 -0
  24. package/build-ngc/core/index.d.ts +2 -0
  25. package/build-ngc/core/index.js +3 -0
  26. package/build-ngc/core/index.js.map +1 -0
  27. package/build-ngc/core/native-module.d.ts +8 -0
  28. package/build-ngc/core/native-module.js +4 -0
  29. package/build-ngc/core/native-module.js.map +1 -0
  30. package/build-ngc/core/types.d.ts +86 -0
  31. package/build-ngc/core/types.js +90 -0
  32. package/build-ngc/core/types.js.map +1 -0
  33. package/native-link.json +12 -0
  34. package/package.json +106 -1
  35. package/src/angular/index.ts +4 -0
  36. package/src/core/haptics.ts +60 -0
  37. package/src/core/index.ts +7 -0
  38. package/src/core/native-module.ts +20 -0
  39. package/src/core/types.ts +88 -0
  40. package/src/react/index.ts +5 -0
  41. package/src/svelte/index.ts +6 -0
  42. package/src/vue/index.ts +4 -0
package/README.md CHANGED
@@ -1,18 +1,116 @@
1
1
  # @symbiote-native/haptics
2
2
 
3
- **Status: skeleton only — not implemented, published early to reserve the npm name.**
3
+ Port of [`expo-haptics`](https://docs.expo.dev/versions/latest/sdk/haptics/) for
4
+ [SymbioteNative](../../README.md) — impact/notification/selection vibration feedback via iOS's
5
+ Taptic Engine and Android's Vibrator API, plus a direct Android haptics-engine path
6
+ (`performAndroidHapticsAsync`). Built the same way as
7
+ [`@symbiote-native/local-auth`](../local-auth): an `expo-modules-core`-based wrapper, free
8
+ functions with no per-instance state and no event stream. Reachable from every adapter — React,
9
+ Vue, Angular — not just React.
4
10
 
5
- Planned port of [`expo-haptics`](https://docs.expo.dev/versions/latest/sdk/haptics/) for
6
- [SymbioteNative](../../README.md) — vibration/haptic feedback via iOS's Taptic Engine and
7
- Android's Vibrator API, reachable from every adapter (React, Vue, Angular), not just React.
8
- Nothing is wired up yet: no `src/`, no dependencies, no build.
11
+ ## Install
9
12
 
10
- ## Planned API
13
+ ```bash
14
+ npm install @symbiote-native/haptics
15
+ ```
16
+
17
+ `expo-haptics` and `expo-modules-core` come along as regular, exact-pinned dependencies — never
18
+ install either yourself, and never add the `expo` meta-package (it bundles its own Metro/Babel
19
+ pipeline that conflicts with this project's own).
20
+
21
+ ### Required one-time step: native autolinking wiring
22
+
23
+ Unlike a plain RN native module, `expo-haptics`' native code is discovered by
24
+ `expo-modules-autolinking`, not RN's own `react-native.config.cjs`/podspec mechanism — this needs
25
+ wiring into the native host app **once**, covering this package and every other
26
+ `expo-modules-core` package with zero further changes:
27
+
28
+ | Platform | Touches |
29
+ |---|---|
30
+ | iOS | `ios/Podfile` — add `use_expo_modules!` |
31
+ | iOS | `AppDelegate.swift` — Expo's runtime-bootstrap hook |
32
+ | Android | `settings.gradle` / `app/build.gradle` — resolve and include the Expo Gradle projects |
33
+ | Android | `MainApplication.kt` — Expo's bootstrap hook, plus a hand-written native-module name map (there's no `expo` meta-package here to auto-generate one) |
34
+
35
+ Full mechanics live in the `symbiote-expo-native-module` project skill. Reference
36
+ implementation: `examples/expo-react/ios/Podfile` and
37
+ `examples/expo-react/android/app/src/main/java/com/canaryexpo/MainApplication.kt` (registers
38
+ `HapticsModule` as `"ExpoHaptics"`).
39
+
40
+ No permission or `Info.plist`/`AndroidManifest.xml` entry is needed on either platform — haptic
41
+ feedback requires none.
42
+
43
+ ## Shape
11
44
 
12
- Free functions, no event stream, no per-instance state — upstream ships four async functions
13
- and three enums, no subscribable feedback source. Signatures below are read straight from
14
- `expo-haptics`'s `Haptics.ts`/`Haptics.types.ts` and are forward-looking reference only; nothing
15
- here exists yet.
45
+ ```
46
+ src/core/ notificationAsync / impactAsync / selectionAsync / performAndroidHapticsAsync,
47
+ plus NotificationFeedbackType / ImpactFeedbackStyle / AndroidHaptics.
48
+ native-module.ts resolves the ExpoHaptics native module via
49
+ expo-modules-core's requireNativeModule.
50
+ src/react/ @symbiote-native/haptics/react — plain re-export of core.
51
+ src/vue/ @symbiote-native/haptics/vue — plain re-export of core.
52
+ src/angular/ @symbiote-native/haptics/angular — plain re-export of core.
53
+ ```
54
+
55
+ Upstream ships four async functions and three enums, not a subscribable sensor — there's
56
+ nothing per-framework to add, so all three adapter entry points are identical re-exports of
57
+ `core`.
58
+
59
+ ## Use it
60
+
61
+ ```tsx
62
+ // React — examples/expo-react/screens/HapticsScreen.tsx
63
+ import { impactAsync, ImpactFeedbackStyle, notificationAsync, NotificationFeedbackType, selectionAsync } from '@symbiote-native/haptics';
64
+
65
+ function HapticsScreen() {
66
+ return (
67
+ <>
68
+ <ActionButton title="Medium" onPress={() => impactAsync(ImpactFeedbackStyle.Medium)} />
69
+ <ActionButton title="Success" onPress={() => notificationAsync(NotificationFeedbackType.Success)} />
70
+ <ActionButton title="Selection" onPress={() => selectionAsync()} />
71
+ </>
72
+ );
73
+ }
74
+ ```
75
+
76
+ ```vue
77
+ <!-- Vue — examples/expo-vue-sfc/screens/HapticsScreen.vue -->
78
+ <script setup lang="ts">
79
+ import { impactAsync, ImpactFeedbackStyle, notificationAsync, NotificationFeedbackType, selectionAsync } from '@symbiote-native/haptics';
80
+
81
+ function fireImpact(style: ImpactFeedbackStyle): void {
82
+ void impactAsync(style);
83
+ }
84
+ </script>
85
+ <template>
86
+ <ActionButton title="Medium" :onPress="() => fireImpact(ImpactFeedbackStyle.Medium)" />
87
+ </template>
88
+ ```
89
+
90
+ ```ts
91
+ // Angular — examples/expo-angular/src/screens/HapticsScreen.ts
92
+ import { Component } from '@angular/core';
93
+ import { impactAsync, ImpactFeedbackStyle, notificationAsync, NotificationFeedbackType } from '@symbiote-native/haptics';
94
+
95
+ @Component({
96
+ selector: 'HapticsScreen',
97
+ template: `<ActionButton title="Medium" (press)="handleImpact()"></ActionButton>`,
98
+ })
99
+ export class HapticsScreen {
100
+ handleImpact(): void {
101
+ impactAsync(ImpactFeedbackStyle.Medium);
102
+ }
103
+ }
104
+ ```
105
+
106
+ Every call is fire-and-forget (no result to await) — a real device is needed to feel the
107
+ feedback, a simulator produces none. Android also gets `performAndroidHapticsAsync`, which
108
+ drives the device haptics engine directly instead of `impactAsync`'s Vibrator simulation; it
109
+ no-ops on every other platform.
110
+
111
+ ## API
112
+
113
+ Free functions, no event stream, no per-instance state:
16
114
 
17
115
  ```ts
18
116
  notificationAsync(type?: NotificationFeedbackType): Promise<void>
@@ -28,16 +126,27 @@ performAndroidHapticsAsync(type: AndroidHaptics): Promise<void>
28
126
  // Android only — no-ops on other platforms. Uses the device haptics engine directly, unlike the Vibrator-based impactAsync.
29
127
  ```
30
128
 
31
- Plus the `NotificationFeedbackType`, `ImpactFeedbackStyle`, and `AndroidHaptics` enums ported
32
- from upstream's `Haptics.types.ts`.
129
+ Plus `NotificationFeedbackType`, `ImpactFeedbackStyle`, `AndroidHaptics` — ported verbatim from
130
+ upstream's `Haptics.types.ts`.
131
+
132
+ ```ts
133
+ import { impactAsync, ImpactFeedbackStyle } from '@symbiote-native/haptics';
134
+ // or the framework-scoped entry points — identical surface, re-exported verbatim:
135
+ import { impactAsync } from '@symbiote-native/haptics/react';
136
+ import { impactAsync } from '@symbiote-native/haptics/vue';
137
+ import { impactAsync } from '@symbiote-native/haptics/angular';
138
+ ```
139
+
140
+ ## Test it
33
141
 
34
- ## Reference
142
+ No Fabric/Descriptor angle at all — haptics is a pure async-function surface, never a view.
143
+ Tests exercise the JS layer via a fake native module in place of the real
144
+ `requireNativeModule` resolution (`vitest`, `src/**/*.test.{ts,tsx}`) — no `installFabric()`, no
145
+ ViewConfig. Native rendering itself is verified on-device (see the parent
146
+ [README](../../README.md)).
35
147
 
36
- - [`@symbiote-native/local-auth`](../local-auth) — the nearest already-implemented sibling
37
- package; shows the shape this package will take once it moves past skeleton (three-entry
38
- `exports` map, `core`/`react`/`vue`/`angular` split, `expo-modules-core` dependency).
39
- - The `symbiote-expo-native-module` project skill
40
- (`../../.claude/skills/symbiote-expo-native-module/SKILL.md`) — the real wrapping mechanics
41
- (depend on `expo-modules-core` only, never the `expo` meta-package; port the JS into `core/`
42
- rather than importing upstream's; how autolinking picks up the native module) that will apply
43
- when implementation starts.
148
+ Native wiring for this package is done across all four `examples/expo-*` canary apps (Android
149
+ 3-layer registration in each app's `build.gradle`/`MainApplication.kt`/`AndroidManifest.xml`,
150
+ plus iOS Podfile/pod install) — not yet ported into the plain, non-Expo public canaries
151
+ (`examples/react`, `examples/vue-sfc`, `examples/vue-tsx`, `examples/angular`), and no
152
+ on-device/simulator automated (Detox) smoke test exists yet, only manual verification.
@@ -0,0 +1 @@
1
+ export * from '../core';
@@ -0,0 +1,4 @@
1
+ // @symbiote-native/haptics/angular: the Angular entry over the framework-agnostic core. Same
2
+ // reasoning as the React/Vue entries — no per-instance state or event stream to wrap in a
3
+ // service, so this is a plain re-export.
4
+ export * from '../core/index.js';
@@ -0,0 +1,24 @@
1
+ import { AndroidHaptics, ImpactFeedbackStyle, NotificationFeedbackType } from './types';
2
+ /**
3
+ * The kind of notification response used in the feedback. On Android it is simulated using
4
+ * `Vibrator`; on iOS it maps directly to `UINotificationFeedbackType`.
5
+ */
6
+ export declare function notificationAsync(type?: NotificationFeedbackType): Promise<void>;
7
+ /**
8
+ * A collision indicator. On Android it is simulated using `Vibrator`; on iOS it maps directly to
9
+ * `UIImpactFeedbackStyle`.
10
+ * @see Android's `Vibrator` API is not recommended for haptics feedback — prefer
11
+ * `performAndroidHapticsAsync`, which drives the device haptics engine directly and does not
12
+ * require the `VIBRATE` permission.
13
+ */
14
+ export declare function impactAsync(style?: ImpactFeedbackStyle): Promise<void>;
15
+ /**
16
+ * Lets the user know a selection change has been registered.
17
+ */
18
+ export declare function selectionAsync(): Promise<void>;
19
+ /**
20
+ * Uses the device haptics engine directly to provide physical feedback to the user. A no-op on
21
+ * every platform except Android.
22
+ * @platform android
23
+ */
24
+ export declare function performAndroidHapticsAsync(type: AndroidHaptics): Promise<void>;
@@ -0,0 +1,50 @@
1
+ import { Platform, UnavailabilityError } from 'expo-modules-core';
2
+ import { expoHaptics } from './native-module.js';
3
+ import { AndroidHaptics, ImpactFeedbackStyle, NotificationFeedbackType } from './types.js';
4
+ const NATIVE_MODULE_NAME = 'expo-haptics';
5
+ /**
6
+ * The kind of notification response used in the feedback. On Android it is simulated using
7
+ * `Vibrator`; on iOS it maps directly to `UINotificationFeedbackType`.
8
+ */
9
+ export async function notificationAsync(type = NotificationFeedbackType.Success) {
10
+ if (!expoHaptics.notificationAsync) {
11
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'notificationAsync');
12
+ }
13
+ await expoHaptics.notificationAsync(type);
14
+ }
15
+ /**
16
+ * A collision indicator. On Android it is simulated using `Vibrator`; on iOS it maps directly to
17
+ * `UIImpactFeedbackStyle`.
18
+ * @see Android's `Vibrator` API is not recommended for haptics feedback — prefer
19
+ * `performAndroidHapticsAsync`, which drives the device haptics engine directly and does not
20
+ * require the `VIBRATE` permission.
21
+ */
22
+ export async function impactAsync(style = ImpactFeedbackStyle.Medium) {
23
+ if (!expoHaptics.impactAsync) {
24
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'impactAsync');
25
+ }
26
+ await expoHaptics.impactAsync(style);
27
+ }
28
+ /**
29
+ * Lets the user know a selection change has been registered.
30
+ */
31
+ export async function selectionAsync() {
32
+ if (!expoHaptics.selectionAsync) {
33
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'selectionAsync');
34
+ }
35
+ await expoHaptics.selectionAsync();
36
+ }
37
+ /**
38
+ * Uses the device haptics engine directly to provide physical feedback to the user. A no-op on
39
+ * every platform except Android.
40
+ * @platform android
41
+ */
42
+ export async function performAndroidHapticsAsync(type) {
43
+ if (Platform.OS !== 'android') {
44
+ return;
45
+ }
46
+ if (!expoHaptics.performHapticsAsync) {
47
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'performHapticsAsync');
48
+ }
49
+ await expoHaptics.performHapticsAsync(type);
50
+ }
@@ -0,0 +1,2 @@
1
+ export { notificationAsync, impactAsync, selectionAsync, performAndroidHapticsAsync, } from './haptics';
2
+ export { NotificationFeedbackType, ImpactFeedbackStyle, AndroidHaptics } from './types';
@@ -0,0 +1,2 @@
1
+ export { notificationAsync, impactAsync, selectionAsync, performAndroidHapticsAsync, } from './haptics.js';
2
+ export { NotificationFeedbackType, ImpactFeedbackStyle, AndroidHaptics } from './types.js';
@@ -0,0 +1,8 @@
1
+ import type { AndroidHaptics, ImpactFeedbackStyle, NotificationFeedbackType } from './types';
2
+ export type INativeHapticsModule = {
3
+ notificationAsync?(type: NotificationFeedbackType): Promise<void>;
4
+ impactAsync?(style: ImpactFeedbackStyle): Promise<void>;
5
+ selectionAsync?(): Promise<void>;
6
+ performHapticsAsync?(type: AndroidHaptics): Promise<void>;
7
+ };
8
+ export declare const expoHaptics: INativeHapticsModule;
@@ -0,0 +1,3 @@
1
+ import { requireNativeModule } from 'expo-modules-core';
2
+ const EXPO_HAPTICS_MODULE_NAME = 'ExpoHaptics';
3
+ export const expoHaptics = requireNativeModule(EXPO_HAPTICS_MODULE_NAME);
@@ -0,0 +1,86 @@
1
+ /**
2
+ * The type of notification feedback generated by a `UINotificationFeedbackGenerator` object.
3
+ * [`UINotificationFeedbackType`](https://developer.apple.com/documentation/uikit/uinotificationfeedbackgenerator/feedbacktype).
4
+ */
5
+ export declare enum NotificationFeedbackType {
6
+ /** A notification feedback type indicating that a task has completed successfully. */
7
+ Success = "success",
8
+ /** A notification feedback type indicating that a task has produced a warning. */
9
+ Warning = "warning",
10
+ /** A notification feedback type indicating that a task has failed. */
11
+ Error = "error"
12
+ }
13
+ /**
14
+ * The mass of the objects in the collision simulated by a `UIImpactFeedbackGenerator` object.
15
+ * [`UIImpactFeedbackStyle`](https://developer.apple.com/documentation/uikit/uiimpactfeedbackgenerator/feedbackstyle).
16
+ */
17
+ export declare enum ImpactFeedbackStyle {
18
+ /** A collision between small, light user interface elements. */
19
+ Light = "light",
20
+ /** A collision between moderately sized user interface elements. */
21
+ Medium = "medium",
22
+ /** A collision between large, heavy user interface elements. */
23
+ Heavy = "heavy",
24
+ /**
25
+ * A collision between user interface elements that are soft, exhibiting a large amount of
26
+ * compression or elasticity.
27
+ */
28
+ Soft = "soft",
29
+ /**
30
+ * A collision between user interface elements that are rigid, exhibiting a small amount of
31
+ * compression or elasticity.
32
+ */
33
+ Rigid = "rigid"
34
+ }
35
+ /**
36
+ * Feedback effects driven directly by Android's haptics engine, via `performAndroidHapticsAsync`.
37
+ * @platform android
38
+ */
39
+ export declare enum AndroidHaptics {
40
+ /** A haptic effect to signal the confirmation or successful completion of a user interaction. */
41
+ Confirm = "confirm",
42
+ /** A haptic effect to signal the rejection or failure of a user interaction. */
43
+ Reject = "reject",
44
+ /** The user has started a gesture (for example, on the soft keyboard). */
45
+ Gesture_Start = "gesture-start",
46
+ /** The user has finished a gesture (for example, on the soft keyboard). */
47
+ Gesture_End = "gesture-end",
48
+ /** The user has toggled a switch or button into the on position. */
49
+ Toggle_On = "toggle-on",
50
+ /** The user has toggled a switch or button into the off position. */
51
+ Toggle_Off = "toggle-off",
52
+ /** The user has pressed either an hour or minute tick of a Clock. */
53
+ Clock_Tick = "clock-tick",
54
+ /** The user has performed a context click on an object. */
55
+ Context_Click = "context-click",
56
+ /** The user has started a drag-and-drop gesture. The drag target has just been "picked up". */
57
+ Drag_Start = "drag-start",
58
+ /** The user has pressed a soft keyboard key. */
59
+ Keyboard_Tap = "keyboard-tap",
60
+ /** The user has pressed a virtual or software keyboard key. */
61
+ Keyboard_Press = "keyboard-press",
62
+ /** The user has released a virtual keyboard key. */
63
+ Keyboard_Release = "keyboard-release",
64
+ /** The user has performed a long press on an object that results in an action being performed. */
65
+ Long_Press = "long-press",
66
+ /** The user has pressed on a virtual on-screen key. */
67
+ Virtual_Key = "virtual-key",
68
+ /** The user has released a virtual key. */
69
+ Virtual_Key_Release = "virtual-key-release",
70
+ /** No haptic feedback should be performed. */
71
+ No_Haptics = "no-haptics",
72
+ /**
73
+ * The user is switching between a series of potential choices. For example, items in a list
74
+ * or discrete points on a slider.
75
+ */
76
+ Segment_Tick = "segment-tick",
77
+ /**
78
+ * The user is switching between a series of many potential choices. For example, minutes on a
79
+ * clock face or individual percentages. Expected to be very soft, so as not to be uncomfortable
80
+ * when performed a lot in quick succession — if the device can't make a suitably soft
81
+ * vibration, it may not make any vibration at all.
82
+ */
83
+ Segment_Frequent_Tick = "segment-frequent-tick",
84
+ /** The user has performed a selection/insertion handle move on a text field. */
85
+ Text_Handle_Move = "text-handle-move"
86
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * The type of notification feedback generated by a `UINotificationFeedbackGenerator` object.
3
+ * [`UINotificationFeedbackType`](https://developer.apple.com/documentation/uikit/uinotificationfeedbackgenerator/feedbacktype).
4
+ */
5
+ export var NotificationFeedbackType;
6
+ (function (NotificationFeedbackType) {
7
+ /** A notification feedback type indicating that a task has completed successfully. */
8
+ NotificationFeedbackType["Success"] = "success";
9
+ /** A notification feedback type indicating that a task has produced a warning. */
10
+ NotificationFeedbackType["Warning"] = "warning";
11
+ /** A notification feedback type indicating that a task has failed. */
12
+ NotificationFeedbackType["Error"] = "error";
13
+ })(NotificationFeedbackType || (NotificationFeedbackType = {}));
14
+ /**
15
+ * The mass of the objects in the collision simulated by a `UIImpactFeedbackGenerator` object.
16
+ * [`UIImpactFeedbackStyle`](https://developer.apple.com/documentation/uikit/uiimpactfeedbackgenerator/feedbackstyle).
17
+ */
18
+ export var ImpactFeedbackStyle;
19
+ (function (ImpactFeedbackStyle) {
20
+ /** A collision between small, light user interface elements. */
21
+ ImpactFeedbackStyle["Light"] = "light";
22
+ /** A collision between moderately sized user interface elements. */
23
+ ImpactFeedbackStyle["Medium"] = "medium";
24
+ /** A collision between large, heavy user interface elements. */
25
+ ImpactFeedbackStyle["Heavy"] = "heavy";
26
+ /**
27
+ * A collision between user interface elements that are soft, exhibiting a large amount of
28
+ * compression or elasticity.
29
+ */
30
+ ImpactFeedbackStyle["Soft"] = "soft";
31
+ /**
32
+ * A collision between user interface elements that are rigid, exhibiting a small amount of
33
+ * compression or elasticity.
34
+ */
35
+ ImpactFeedbackStyle["Rigid"] = "rigid";
36
+ })(ImpactFeedbackStyle || (ImpactFeedbackStyle = {}));
37
+ /**
38
+ * Feedback effects driven directly by Android's haptics engine, via `performAndroidHapticsAsync`.
39
+ * @platform android
40
+ */
41
+ export var AndroidHaptics;
42
+ (function (AndroidHaptics) {
43
+ /** A haptic effect to signal the confirmation or successful completion of a user interaction. */
44
+ AndroidHaptics["Confirm"] = "confirm";
45
+ /** A haptic effect to signal the rejection or failure of a user interaction. */
46
+ AndroidHaptics["Reject"] = "reject";
47
+ /** The user has started a gesture (for example, on the soft keyboard). */
48
+ AndroidHaptics["Gesture_Start"] = "gesture-start";
49
+ /** The user has finished a gesture (for example, on the soft keyboard). */
50
+ AndroidHaptics["Gesture_End"] = "gesture-end";
51
+ /** The user has toggled a switch or button into the on position. */
52
+ AndroidHaptics["Toggle_On"] = "toggle-on";
53
+ /** The user has toggled a switch or button into the off position. */
54
+ AndroidHaptics["Toggle_Off"] = "toggle-off";
55
+ /** The user has pressed either an hour or minute tick of a Clock. */
56
+ AndroidHaptics["Clock_Tick"] = "clock-tick";
57
+ /** The user has performed a context click on an object. */
58
+ AndroidHaptics["Context_Click"] = "context-click";
59
+ /** The user has started a drag-and-drop gesture. The drag target has just been "picked up". */
60
+ AndroidHaptics["Drag_Start"] = "drag-start";
61
+ /** The user has pressed a soft keyboard key. */
62
+ AndroidHaptics["Keyboard_Tap"] = "keyboard-tap";
63
+ /** The user has pressed a virtual or software keyboard key. */
64
+ AndroidHaptics["Keyboard_Press"] = "keyboard-press";
65
+ /** The user has released a virtual keyboard key. */
66
+ AndroidHaptics["Keyboard_Release"] = "keyboard-release";
67
+ /** The user has performed a long press on an object that results in an action being performed. */
68
+ AndroidHaptics["Long_Press"] = "long-press";
69
+ /** The user has pressed on a virtual on-screen key. */
70
+ AndroidHaptics["Virtual_Key"] = "virtual-key";
71
+ /** The user has released a virtual key. */
72
+ AndroidHaptics["Virtual_Key_Release"] = "virtual-key-release";
73
+ /** No haptic feedback should be performed. */
74
+ AndroidHaptics["No_Haptics"] = "no-haptics";
75
+ /**
76
+ * The user is switching between a series of potential choices. For example, items in a list
77
+ * or discrete points on a slider.
78
+ */
79
+ AndroidHaptics["Segment_Tick"] = "segment-tick";
80
+ /**
81
+ * The user is switching between a series of many potential choices. For example, minutes on a
82
+ * clock face or individual percentages. Expected to be very soft, so as not to be uncomfortable
83
+ * when performed a lot in quick succession — if the device can't make a suitably soft
84
+ * vibration, it may not make any vibration at all.
85
+ */
86
+ AndroidHaptics["Segment_Frequent_Tick"] = "segment-frequent-tick";
87
+ /** The user has performed a selection/insertion handle move on a text field. */
88
+ AndroidHaptics["Text_Handle_Move"] = "text-handle-move";
89
+ })(AndroidHaptics || (AndroidHaptics = {}));
@@ -0,0 +1 @@
1
+ export * from '../core';
@@ -0,0 +1,5 @@
1
+ // @symbiote-native/haptics/react: the React entry over the framework-agnostic core.
2
+ // Upstream ships free async functions and three enums, no per-instance state and no event
3
+ // stream (same shape as @symbiote-native/local-auth) — there is nothing for a hook to wrap, so
4
+ // this is a plain re-export.
5
+ export * from '../core/index.js';
@@ -0,0 +1 @@
1
+ export * from '../core';
@@ -0,0 +1,6 @@
1
+ // @symbiote-native/haptics/svelte: the Svelte entry over the framework-agnostic core.
2
+ // Upstream ships free async functions and three enums, no per-instance state and no event
3
+ // stream (same shape as @symbiote-native/local-auth) — there is nothing for a rune to wrap, so
4
+ // this is a plain re-export. Svelte's lifecycle bucket is `runes/` (adapters/svelte/src/runes —
5
+ // never React's `hooks` or Vue's `composables`), and this package has none to fill.
6
+ export * from '../core/index.js';
@@ -0,0 +1 @@
1
+ export * from '../core';
@@ -0,0 +1,4 @@
1
+ // @symbiote-native/haptics/vue: the Vue entry over the framework-agnostic core. Same reasoning
2
+ // as the React entry — no per-instance state or event stream to wire onto Vue's reactivity, so
3
+ // this is a plain re-export.
4
+ export * from '../core/index.js';
@@ -0,0 +1 @@
1
+ export * from '../core';
@@ -0,0 +1,5 @@
1
+ // @symbiote-native/haptics/angular: the Angular entry over the framework-agnostic core. Same
2
+ // reasoning as the React/Vue entries — no per-instance state or event stream to wrap in a
3
+ // service, so this is a plain re-export.
4
+ export * from '../core';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/angular/index.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,0FAA0F;AAC1F,yCAAyC;AACzC,cAAc,SAAS,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { AndroidHaptics, ImpactFeedbackStyle, NotificationFeedbackType } from './types';
2
+ /**
3
+ * The kind of notification response used in the feedback. On Android it is simulated using
4
+ * `Vibrator`; on iOS it maps directly to `UINotificationFeedbackType`.
5
+ */
6
+ export declare function notificationAsync(type?: NotificationFeedbackType): Promise<void>;
7
+ /**
8
+ * A collision indicator. On Android it is simulated using `Vibrator`; on iOS it maps directly to
9
+ * `UIImpactFeedbackStyle`.
10
+ * @see Android's `Vibrator` API is not recommended for haptics feedback — prefer
11
+ * `performAndroidHapticsAsync`, which drives the device haptics engine directly and does not
12
+ * require the `VIBRATE` permission.
13
+ */
14
+ export declare function impactAsync(style?: ImpactFeedbackStyle): Promise<void>;
15
+ /**
16
+ * Lets the user know a selection change has been registered.
17
+ */
18
+ export declare function selectionAsync(): Promise<void>;
19
+ /**
20
+ * Uses the device haptics engine directly to provide physical feedback to the user. A no-op on
21
+ * every platform except Android.
22
+ * @platform android
23
+ */
24
+ export declare function performAndroidHapticsAsync(type: AndroidHaptics): Promise<void>;
@@ -0,0 +1,51 @@
1
+ import { Platform, UnavailabilityError } from 'expo-modules-core';
2
+ import { expoHaptics } from './native-module';
3
+ import { AndroidHaptics, ImpactFeedbackStyle, NotificationFeedbackType } from './types';
4
+ const NATIVE_MODULE_NAME = 'expo-haptics';
5
+ /**
6
+ * The kind of notification response used in the feedback. On Android it is simulated using
7
+ * `Vibrator`; on iOS it maps directly to `UINotificationFeedbackType`.
8
+ */
9
+ export async function notificationAsync(type = NotificationFeedbackType.Success) {
10
+ if (!expoHaptics.notificationAsync) {
11
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'notificationAsync');
12
+ }
13
+ await expoHaptics.notificationAsync(type);
14
+ }
15
+ /**
16
+ * A collision indicator. On Android it is simulated using `Vibrator`; on iOS it maps directly to
17
+ * `UIImpactFeedbackStyle`.
18
+ * @see Android's `Vibrator` API is not recommended for haptics feedback — prefer
19
+ * `performAndroidHapticsAsync`, which drives the device haptics engine directly and does not
20
+ * require the `VIBRATE` permission.
21
+ */
22
+ export async function impactAsync(style = ImpactFeedbackStyle.Medium) {
23
+ if (!expoHaptics.impactAsync) {
24
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'impactAsync');
25
+ }
26
+ await expoHaptics.impactAsync(style);
27
+ }
28
+ /**
29
+ * Lets the user know a selection change has been registered.
30
+ */
31
+ export async function selectionAsync() {
32
+ if (!expoHaptics.selectionAsync) {
33
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'selectionAsync');
34
+ }
35
+ await expoHaptics.selectionAsync();
36
+ }
37
+ /**
38
+ * Uses the device haptics engine directly to provide physical feedback to the user. A no-op on
39
+ * every platform except Android.
40
+ * @platform android
41
+ */
42
+ export async function performAndroidHapticsAsync(type) {
43
+ if (Platform.OS !== 'android') {
44
+ return;
45
+ }
46
+ if (!expoHaptics.performHapticsAsync) {
47
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'performHapticsAsync');
48
+ }
49
+ await expoHaptics.performHapticsAsync(type);
50
+ }
51
+ //# sourceMappingURL=haptics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"haptics.js","sourceRoot":"","sources":["../../src/core/haptics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAElE,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAC;AAExF,MAAM,kBAAkB,GAAG,cAAc,CAAC;AAE1C;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAAiC,wBAAwB,CAAC,OAAO;IAEjE,IAAI,CAAC,WAAW,CAAC,iBAAiB,EAAE,CAAC;QACnC,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,mBAAmB,CAAC,CAAC;IACzE,CAAC;IACD,MAAM,WAAW,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,QAA6B,mBAAmB,CAAC,MAAM;IAEvD,IAAI,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC;QAC7B,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,aAAa,CAAC,CAAC;IACnE,CAAC;IACD,MAAM,WAAW,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;AACvC,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,IAAI,CAAC,WAAW,CAAC,cAAc,EAAE,CAAC;QAChC,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,gBAAgB,CAAC,CAAC;IACtE,CAAC;IACD,MAAM,WAAW,CAAC,cAAc,EAAE,CAAC;AACrC,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAAC,IAAoB;IACnE,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;QAC9B,OAAO;IACT,CAAC;IACD,IAAI,CAAC,WAAW,CAAC,mBAAmB,EAAE,CAAC;QACrC,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,qBAAqB,CAAC,CAAC;IAC3E,CAAC;IACD,MAAM,WAAW,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { notificationAsync, impactAsync, selectionAsync, performAndroidHapticsAsync, } from './haptics';
2
+ export { NotificationFeedbackType, ImpactFeedbackStyle, AndroidHaptics } from './types';
@@ -0,0 +1,3 @@
1
+ export { notificationAsync, impactAsync, selectionAsync, performAndroidHapticsAsync, } from './haptics';
2
+ export { NotificationFeedbackType, ImpactFeedbackStyle, AndroidHaptics } from './types';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,WAAW,EACX,cAAc,EACd,0BAA0B,GAC3B,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { AndroidHaptics, ImpactFeedbackStyle, NotificationFeedbackType } from './types';
2
+ export type INativeHapticsModule = {
3
+ notificationAsync?(type: NotificationFeedbackType): Promise<void>;
4
+ impactAsync?(style: ImpactFeedbackStyle): Promise<void>;
5
+ selectionAsync?(): Promise<void>;
6
+ performHapticsAsync?(type: AndroidHaptics): Promise<void>;
7
+ };
8
+ export declare const expoHaptics: INativeHapticsModule;
@@ -0,0 +1,4 @@
1
+ import { requireNativeModule } from 'expo-modules-core';
2
+ const EXPO_HAPTICS_MODULE_NAME = 'ExpoHaptics';
3
+ export const expoHaptics = requireNativeModule(EXPO_HAPTICS_MODULE_NAME);
4
+ //# sourceMappingURL=native-module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-module.js","sourceRoot":"","sources":["../../src/core/native-module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAGxD,MAAM,wBAAwB,GAAG,aAAa,CAAC;AAgB/C,MAAM,CAAC,MAAM,WAAW,GAAG,mBAAmB,CAAuB,wBAAwB,CAAC,CAAC"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * The type of notification feedback generated by a `UINotificationFeedbackGenerator` object.
3
+ * [`UINotificationFeedbackType`](https://developer.apple.com/documentation/uikit/uinotificationfeedbackgenerator/feedbacktype).
4
+ */
5
+ export declare enum NotificationFeedbackType {
6
+ /** A notification feedback type indicating that a task has completed successfully. */
7
+ Success = "success",
8
+ /** A notification feedback type indicating that a task has produced a warning. */
9
+ Warning = "warning",
10
+ /** A notification feedback type indicating that a task has failed. */
11
+ Error = "error"
12
+ }
13
+ /**
14
+ * The mass of the objects in the collision simulated by a `UIImpactFeedbackGenerator` object.
15
+ * [`UIImpactFeedbackStyle`](https://developer.apple.com/documentation/uikit/uiimpactfeedbackgenerator/feedbackstyle).
16
+ */
17
+ export declare enum ImpactFeedbackStyle {
18
+ /** A collision between small, light user interface elements. */
19
+ Light = "light",
20
+ /** A collision between moderately sized user interface elements. */
21
+ Medium = "medium",
22
+ /** A collision between large, heavy user interface elements. */
23
+ Heavy = "heavy",
24
+ /**
25
+ * A collision between user interface elements that are soft, exhibiting a large amount of
26
+ * compression or elasticity.
27
+ */
28
+ Soft = "soft",
29
+ /**
30
+ * A collision between user interface elements that are rigid, exhibiting a small amount of
31
+ * compression or elasticity.
32
+ */
33
+ Rigid = "rigid"
34
+ }
35
+ /**
36
+ * Feedback effects driven directly by Android's haptics engine, via `performAndroidHapticsAsync`.
37
+ * @platform android
38
+ */
39
+ export declare enum AndroidHaptics {
40
+ /** A haptic effect to signal the confirmation or successful completion of a user interaction. */
41
+ Confirm = "confirm",
42
+ /** A haptic effect to signal the rejection or failure of a user interaction. */
43
+ Reject = "reject",
44
+ /** The user has started a gesture (for example, on the soft keyboard). */
45
+ Gesture_Start = "gesture-start",
46
+ /** The user has finished a gesture (for example, on the soft keyboard). */
47
+ Gesture_End = "gesture-end",
48
+ /** The user has toggled a switch or button into the on position. */
49
+ Toggle_On = "toggle-on",
50
+ /** The user has toggled a switch or button into the off position. */
51
+ Toggle_Off = "toggle-off",
52
+ /** The user has pressed either an hour or minute tick of a Clock. */
53
+ Clock_Tick = "clock-tick",
54
+ /** The user has performed a context click on an object. */
55
+ Context_Click = "context-click",
56
+ /** The user has started a drag-and-drop gesture. The drag target has just been "picked up". */
57
+ Drag_Start = "drag-start",
58
+ /** The user has pressed a soft keyboard key. */
59
+ Keyboard_Tap = "keyboard-tap",
60
+ /** The user has pressed a virtual or software keyboard key. */
61
+ Keyboard_Press = "keyboard-press",
62
+ /** The user has released a virtual keyboard key. */
63
+ Keyboard_Release = "keyboard-release",
64
+ /** The user has performed a long press on an object that results in an action being performed. */
65
+ Long_Press = "long-press",
66
+ /** The user has pressed on a virtual on-screen key. */
67
+ Virtual_Key = "virtual-key",
68
+ /** The user has released a virtual key. */
69
+ Virtual_Key_Release = "virtual-key-release",
70
+ /** No haptic feedback should be performed. */
71
+ No_Haptics = "no-haptics",
72
+ /**
73
+ * The user is switching between a series of potential choices. For example, items in a list
74
+ * or discrete points on a slider.
75
+ */
76
+ Segment_Tick = "segment-tick",
77
+ /**
78
+ * The user is switching between a series of many potential choices. For example, minutes on a
79
+ * clock face or individual percentages. Expected to be very soft, so as not to be uncomfortable
80
+ * when performed a lot in quick succession — if the device can't make a suitably soft
81
+ * vibration, it may not make any vibration at all.
82
+ */
83
+ Segment_Frequent_Tick = "segment-frequent-tick",
84
+ /** The user has performed a selection/insertion handle move on a text field. */
85
+ Text_Handle_Move = "text-handle-move"
86
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * The type of notification feedback generated by a `UINotificationFeedbackGenerator` object.
3
+ * [`UINotificationFeedbackType`](https://developer.apple.com/documentation/uikit/uinotificationfeedbackgenerator/feedbacktype).
4
+ */
5
+ export var NotificationFeedbackType;
6
+ (function (NotificationFeedbackType) {
7
+ /** A notification feedback type indicating that a task has completed successfully. */
8
+ NotificationFeedbackType["Success"] = "success";
9
+ /** A notification feedback type indicating that a task has produced a warning. */
10
+ NotificationFeedbackType["Warning"] = "warning";
11
+ /** A notification feedback type indicating that a task has failed. */
12
+ NotificationFeedbackType["Error"] = "error";
13
+ })(NotificationFeedbackType || (NotificationFeedbackType = {}));
14
+ /**
15
+ * The mass of the objects in the collision simulated by a `UIImpactFeedbackGenerator` object.
16
+ * [`UIImpactFeedbackStyle`](https://developer.apple.com/documentation/uikit/uiimpactfeedbackgenerator/feedbackstyle).
17
+ */
18
+ export var ImpactFeedbackStyle;
19
+ (function (ImpactFeedbackStyle) {
20
+ /** A collision between small, light user interface elements. */
21
+ ImpactFeedbackStyle["Light"] = "light";
22
+ /** A collision between moderately sized user interface elements. */
23
+ ImpactFeedbackStyle["Medium"] = "medium";
24
+ /** A collision between large, heavy user interface elements. */
25
+ ImpactFeedbackStyle["Heavy"] = "heavy";
26
+ /**
27
+ * A collision between user interface elements that are soft, exhibiting a large amount of
28
+ * compression or elasticity.
29
+ */
30
+ ImpactFeedbackStyle["Soft"] = "soft";
31
+ /**
32
+ * A collision between user interface elements that are rigid, exhibiting a small amount of
33
+ * compression or elasticity.
34
+ */
35
+ ImpactFeedbackStyle["Rigid"] = "rigid";
36
+ })(ImpactFeedbackStyle || (ImpactFeedbackStyle = {}));
37
+ /**
38
+ * Feedback effects driven directly by Android's haptics engine, via `performAndroidHapticsAsync`.
39
+ * @platform android
40
+ */
41
+ export var AndroidHaptics;
42
+ (function (AndroidHaptics) {
43
+ /** A haptic effect to signal the confirmation or successful completion of a user interaction. */
44
+ AndroidHaptics["Confirm"] = "confirm";
45
+ /** A haptic effect to signal the rejection or failure of a user interaction. */
46
+ AndroidHaptics["Reject"] = "reject";
47
+ /** The user has started a gesture (for example, on the soft keyboard). */
48
+ AndroidHaptics["Gesture_Start"] = "gesture-start";
49
+ /** The user has finished a gesture (for example, on the soft keyboard). */
50
+ AndroidHaptics["Gesture_End"] = "gesture-end";
51
+ /** The user has toggled a switch or button into the on position. */
52
+ AndroidHaptics["Toggle_On"] = "toggle-on";
53
+ /** The user has toggled a switch or button into the off position. */
54
+ AndroidHaptics["Toggle_Off"] = "toggle-off";
55
+ /** The user has pressed either an hour or minute tick of a Clock. */
56
+ AndroidHaptics["Clock_Tick"] = "clock-tick";
57
+ /** The user has performed a context click on an object. */
58
+ AndroidHaptics["Context_Click"] = "context-click";
59
+ /** The user has started a drag-and-drop gesture. The drag target has just been "picked up". */
60
+ AndroidHaptics["Drag_Start"] = "drag-start";
61
+ /** The user has pressed a soft keyboard key. */
62
+ AndroidHaptics["Keyboard_Tap"] = "keyboard-tap";
63
+ /** The user has pressed a virtual or software keyboard key. */
64
+ AndroidHaptics["Keyboard_Press"] = "keyboard-press";
65
+ /** The user has released a virtual keyboard key. */
66
+ AndroidHaptics["Keyboard_Release"] = "keyboard-release";
67
+ /** The user has performed a long press on an object that results in an action being performed. */
68
+ AndroidHaptics["Long_Press"] = "long-press";
69
+ /** The user has pressed on a virtual on-screen key. */
70
+ AndroidHaptics["Virtual_Key"] = "virtual-key";
71
+ /** The user has released a virtual key. */
72
+ AndroidHaptics["Virtual_Key_Release"] = "virtual-key-release";
73
+ /** No haptic feedback should be performed. */
74
+ AndroidHaptics["No_Haptics"] = "no-haptics";
75
+ /**
76
+ * The user is switching between a series of potential choices. For example, items in a list
77
+ * or discrete points on a slider.
78
+ */
79
+ AndroidHaptics["Segment_Tick"] = "segment-tick";
80
+ /**
81
+ * The user is switching between a series of many potential choices. For example, minutes on a
82
+ * clock face or individual percentages. Expected to be very soft, so as not to be uncomfortable
83
+ * when performed a lot in quick succession — if the device can't make a suitably soft
84
+ * vibration, it may not make any vibration at all.
85
+ */
86
+ AndroidHaptics["Segment_Frequent_Tick"] = "segment-frequent-tick";
87
+ /** The user has performed a selection/insertion handle move on a text field. */
88
+ AndroidHaptics["Text_Handle_Move"] = "text-handle-move";
89
+ })(AndroidHaptics || (AndroidHaptics = {}));
90
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,CAAN,IAAY,wBAOX;AAPD,WAAY,wBAAwB;IAClC,sFAAsF;IACtF,+CAAmB,CAAA;IACnB,kFAAkF;IAClF,+CAAmB,CAAA;IACnB,sEAAsE;IACtE,2CAAe,CAAA;AACjB,CAAC,EAPW,wBAAwB,KAAxB,wBAAwB,QAOnC;AAED;;;GAGG;AACH,MAAM,CAAN,IAAY,mBAiBX;AAjBD,WAAY,mBAAmB;IAC7B,gEAAgE;IAChE,sCAAe,CAAA;IACf,oEAAoE;IACpE,wCAAiB,CAAA;IACjB,gEAAgE;IAChE,sCAAe,CAAA;IACf;;;OAGG;IACH,oCAAa,CAAA;IACb;;;OAGG;IACH,sCAAe,CAAA;AACjB,CAAC,EAjBW,mBAAmB,KAAnB,mBAAmB,QAiB9B;AAED;;;GAGG;AACH,MAAM,CAAN,IAAY,cA+CX;AA/CD,WAAY,cAAc;IACxB,iGAAiG;IACjG,qCAAmB,CAAA;IACnB,gFAAgF;IAChF,mCAAiB,CAAA;IACjB,0EAA0E;IAC1E,iDAA+B,CAAA;IAC/B,2EAA2E;IAC3E,6CAA2B,CAAA;IAC3B,oEAAoE;IACpE,yCAAuB,CAAA;IACvB,qEAAqE;IACrE,2CAAyB,CAAA;IACzB,qEAAqE;IACrE,2CAAyB,CAAA;IACzB,2DAA2D;IAC3D,iDAA+B,CAAA;IAC/B,+FAA+F;IAC/F,2CAAyB,CAAA;IACzB,gDAAgD;IAChD,+CAA6B,CAAA;IAC7B,+DAA+D;IAC/D,mDAAiC,CAAA;IACjC,oDAAoD;IACpD,uDAAqC,CAAA;IACrC,kGAAkG;IAClG,2CAAyB,CAAA;IACzB,uDAAuD;IACvD,6CAA2B,CAAA;IAC3B,2CAA2C;IAC3C,6DAA2C,CAAA;IAC3C,8CAA8C;IAC9C,2CAAyB,CAAA;IACzB;;;OAGG;IACH,+CAA6B,CAAA;IAC7B;;;;;OAKG;IACH,iEAA+C,CAAA;IAC/C,gFAAgF;IAChF,uDAAqC,CAAA;AACvC,CAAC,EA/CW,cAAc,KAAd,cAAc,QA+CzB"}
@@ -0,0 +1,12 @@
1
+ {
2
+ "android": {
3
+ "gradleProjectName": "expo-haptics",
4
+ "modules": [
5
+ {
6
+ "importPath": "expo.modules.haptics.HapticsModule",
7
+ "className": "HapticsModule",
8
+ "nativeName": "ExpoHaptics"
9
+ }
10
+ ]
11
+ }
12
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symbiote-native/haptics",
3
- "version": "0.0.1",
3
+ "version": "0.2.0",
4
4
  "description": "expo-haptics wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, and Angular adapters. Impact/notification/selection vibration feedback via iOS's Taptic Engine and Android's Vibrator API.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -13,7 +13,112 @@
13
13
  "url": "https://github.com/OneEyed1366/symbiote-native/issues"
14
14
  },
15
15
  "author": "Andrey Prokopenko <psevdoproger@gmail.com>",
16
+ "type": "module",
17
+ "main": "./build/core/index.js",
18
+ "module": "./build/core/index.js",
19
+ "types": "./build/core/index.d.ts",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./build/core/index.d.ts",
23
+ "default": "./build/core/index.js"
24
+ },
25
+ "./vue": {
26
+ "types": "./build/vue/index.d.ts",
27
+ "default": "./build/vue/index.js"
28
+ },
29
+ "./react": {
30
+ "types": "./build/react/index.d.ts",
31
+ "default": "./build/react/index.js"
32
+ },
33
+ "./svelte": {
34
+ "types": "./build/svelte/index.d.ts",
35
+ "default": "./build/svelte/index.js"
36
+ },
37
+ "./angular": {
38
+ "types": "./build-ngc/angular/index.d.ts",
39
+ "react-native": "./build-ngc/angular/index.js",
40
+ "default": "./src/angular/index.ts"
41
+ }
42
+ },
43
+ "files": [
44
+ "src",
45
+ "build",
46
+ "build-ngc",
47
+ "native-link.json",
48
+ "!src/**/*.test.*",
49
+ "!src/**/*.spec.*",
50
+ "!src/**/*.detox.*"
51
+ ],
16
52
  "publishConfig": {
17
53
  "access": "public"
54
+ },
55
+ "dependencies": {
56
+ "expo-haptics": "57.0.1",
57
+ "expo-modules-core": "57.0.5"
58
+ },
59
+ "peerDependencies": {
60
+ "@symbiote-native/angular": ">=0.6.2",
61
+ "@symbiote-native/engine": ">=0.1.7",
62
+ "@symbiote-native/react": ">=0.2.8",
63
+ "@symbiote-native/svelte": ">=0.1.0",
64
+ "@symbiote-native/vue": ">=0.3.8",
65
+ "@angular/core": ">=20",
66
+ "@vue/runtime-core": "^3.5.13",
67
+ "react": ">=19.0.0",
68
+ "react-native": ">=0.86",
69
+ "svelte": ">=5.56.0",
70
+ "vue": ">=3.5.0"
71
+ },
72
+ "peerDependenciesMeta": {
73
+ "@symbiote-native/angular": {
74
+ "optional": true
75
+ },
76
+ "@symbiote-native/react": {
77
+ "optional": true
78
+ },
79
+ "@symbiote-native/svelte": {
80
+ "optional": true
81
+ },
82
+ "@symbiote-native/vue": {
83
+ "optional": true
84
+ },
85
+ "@angular/core": {
86
+ "optional": true
87
+ },
88
+ "@vue/runtime-core": {
89
+ "optional": true
90
+ },
91
+ "react": {
92
+ "optional": true
93
+ },
94
+ "svelte": {
95
+ "optional": true
96
+ },
97
+ "vue": {
98
+ "optional": true
99
+ }
100
+ },
101
+ "devDependencies": {
102
+ "@angular/compiler": "~22.0.8",
103
+ "@angular/compiler-cli": "~22.0.8",
104
+ "@angular/core": "~22.0.8",
105
+ "@types/node": "^26.0.0",
106
+ "@types/react": "^19.2.0",
107
+ "@vue/runtime-core": "^3.5.13",
108
+ "react": "19.2.3",
109
+ "svelte": "^5.56.0",
110
+ "typescript": "~6.0.0",
111
+ "@symbiote-native/angular": "0.7.0",
112
+ "@symbiote-native/engine": "0.2.0",
113
+ "@symbiote-native/react": "0.3.0",
114
+ "@symbiote-native/svelte": "0.2.0",
115
+ "@symbiote-native/test-utils": "0.1.6",
116
+ "@symbiote-native/vue": "0.4.0"
117
+ },
118
+ "scripts": {
119
+ "typecheck": "tsc --build",
120
+ "clean": "rm -rf build-ngc",
121
+ "ng:build": "pnpm run clean && ngc -p tsconfig.angular.json",
122
+ "format": "prettier --write \"src/**/*.{ts,tsx}\""
18
123
  }
19
124
  }
@@ -0,0 +1,4 @@
1
+ // @symbiote-native/haptics/angular: the Angular entry over the framework-agnostic core. Same
2
+ // reasoning as the React/Vue entries — no per-instance state or event stream to wrap in a
3
+ // service, so this is a plain re-export.
4
+ export * from '../core';
@@ -0,0 +1,60 @@
1
+ import { Platform, UnavailabilityError } from 'expo-modules-core';
2
+
3
+ import { expoHaptics } from './native-module';
4
+ import { AndroidHaptics, ImpactFeedbackStyle, NotificationFeedbackType } from './types';
5
+
6
+ const NATIVE_MODULE_NAME = 'expo-haptics';
7
+
8
+ /**
9
+ * The kind of notification response used in the feedback. On Android it is simulated using
10
+ * `Vibrator`; on iOS it maps directly to `UINotificationFeedbackType`.
11
+ */
12
+ export async function notificationAsync(
13
+ type: NotificationFeedbackType = NotificationFeedbackType.Success,
14
+ ): Promise<void> {
15
+ if (!expoHaptics.notificationAsync) {
16
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'notificationAsync');
17
+ }
18
+ await expoHaptics.notificationAsync(type);
19
+ }
20
+
21
+ /**
22
+ * A collision indicator. On Android it is simulated using `Vibrator`; on iOS it maps directly to
23
+ * `UIImpactFeedbackStyle`.
24
+ * @see Android's `Vibrator` API is not recommended for haptics feedback — prefer
25
+ * `performAndroidHapticsAsync`, which drives the device haptics engine directly and does not
26
+ * require the `VIBRATE` permission.
27
+ */
28
+ export async function impactAsync(
29
+ style: ImpactFeedbackStyle = ImpactFeedbackStyle.Medium,
30
+ ): Promise<void> {
31
+ if (!expoHaptics.impactAsync) {
32
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'impactAsync');
33
+ }
34
+ await expoHaptics.impactAsync(style);
35
+ }
36
+
37
+ /**
38
+ * Lets the user know a selection change has been registered.
39
+ */
40
+ export async function selectionAsync(): Promise<void> {
41
+ if (!expoHaptics.selectionAsync) {
42
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'selectionAsync');
43
+ }
44
+ await expoHaptics.selectionAsync();
45
+ }
46
+
47
+ /**
48
+ * Uses the device haptics engine directly to provide physical feedback to the user. A no-op on
49
+ * every platform except Android.
50
+ * @platform android
51
+ */
52
+ export async function performAndroidHapticsAsync(type: AndroidHaptics): Promise<void> {
53
+ if (Platform.OS !== 'android') {
54
+ return;
55
+ }
56
+ if (!expoHaptics.performHapticsAsync) {
57
+ throw new UnavailabilityError(NATIVE_MODULE_NAME, 'performHapticsAsync');
58
+ }
59
+ await expoHaptics.performHapticsAsync(type);
60
+ }
@@ -0,0 +1,7 @@
1
+ export {
2
+ notificationAsync,
3
+ impactAsync,
4
+ selectionAsync,
5
+ performAndroidHapticsAsync,
6
+ } from './haptics';
7
+ export { NotificationFeedbackType, ImpactFeedbackStyle, AndroidHaptics } from './types';
@@ -0,0 +1,20 @@
1
+ import { requireNativeModule } from 'expo-modules-core';
2
+ import type { AndroidHaptics, ImpactFeedbackStyle, NotificationFeedbackType } from './types';
3
+
4
+ const EXPO_HAPTICS_MODULE_NAME = 'ExpoHaptics';
5
+
6
+ // Every method is optional — each call site checks for its presence before calling through and
7
+ // throws an UnavailabilityError itself, matching upstream's own per-platform capability checks
8
+ // rather than assuming the native module implements the whole surface (same convention as
9
+ // packages/local-auth/src/core/native-module.ts). `performHapticsAsync` in particular only
10
+ // exists on the Android native module (HapticsModule.kt) — iOS's HapticsModule.swift has no
11
+ // equivalent, since `performAndroidHapticsAsync` short-circuits on non-Android platforms before
12
+ // ever reaching this call.
13
+ export type INativeHapticsModule = {
14
+ notificationAsync?(type: NotificationFeedbackType): Promise<void>;
15
+ impactAsync?(style: ImpactFeedbackStyle): Promise<void>;
16
+ selectionAsync?(): Promise<void>;
17
+ performHapticsAsync?(type: AndroidHaptics): Promise<void>;
18
+ };
19
+
20
+ export const expoHaptics = requireNativeModule<INativeHapticsModule>(EXPO_HAPTICS_MODULE_NAME);
@@ -0,0 +1,88 @@
1
+ /**
2
+ * The type of notification feedback generated by a `UINotificationFeedbackGenerator` object.
3
+ * [`UINotificationFeedbackType`](https://developer.apple.com/documentation/uikit/uinotificationfeedbackgenerator/feedbacktype).
4
+ */
5
+ export enum NotificationFeedbackType {
6
+ /** A notification feedback type indicating that a task has completed successfully. */
7
+ Success = 'success',
8
+ /** A notification feedback type indicating that a task has produced a warning. */
9
+ Warning = 'warning',
10
+ /** A notification feedback type indicating that a task has failed. */
11
+ Error = 'error',
12
+ }
13
+
14
+ /**
15
+ * The mass of the objects in the collision simulated by a `UIImpactFeedbackGenerator` object.
16
+ * [`UIImpactFeedbackStyle`](https://developer.apple.com/documentation/uikit/uiimpactfeedbackgenerator/feedbackstyle).
17
+ */
18
+ export enum ImpactFeedbackStyle {
19
+ /** A collision between small, light user interface elements. */
20
+ Light = 'light',
21
+ /** A collision between moderately sized user interface elements. */
22
+ Medium = 'medium',
23
+ /** A collision between large, heavy user interface elements. */
24
+ Heavy = 'heavy',
25
+ /**
26
+ * A collision between user interface elements that are soft, exhibiting a large amount of
27
+ * compression or elasticity.
28
+ */
29
+ Soft = 'soft',
30
+ /**
31
+ * A collision between user interface elements that are rigid, exhibiting a small amount of
32
+ * compression or elasticity.
33
+ */
34
+ Rigid = 'rigid',
35
+ }
36
+
37
+ /**
38
+ * Feedback effects driven directly by Android's haptics engine, via `performAndroidHapticsAsync`.
39
+ * @platform android
40
+ */
41
+ export enum AndroidHaptics {
42
+ /** A haptic effect to signal the confirmation or successful completion of a user interaction. */
43
+ Confirm = 'confirm',
44
+ /** A haptic effect to signal the rejection or failure of a user interaction. */
45
+ Reject = 'reject',
46
+ /** The user has started a gesture (for example, on the soft keyboard). */
47
+ Gesture_Start = 'gesture-start',
48
+ /** The user has finished a gesture (for example, on the soft keyboard). */
49
+ Gesture_End = 'gesture-end',
50
+ /** The user has toggled a switch or button into the on position. */
51
+ Toggle_On = 'toggle-on',
52
+ /** The user has toggled a switch or button into the off position. */
53
+ Toggle_Off = 'toggle-off',
54
+ /** The user has pressed either an hour or minute tick of a Clock. */
55
+ Clock_Tick = 'clock-tick',
56
+ /** The user has performed a context click on an object. */
57
+ Context_Click = 'context-click',
58
+ /** The user has started a drag-and-drop gesture. The drag target has just been "picked up". */
59
+ Drag_Start = 'drag-start',
60
+ /** The user has pressed a soft keyboard key. */
61
+ Keyboard_Tap = 'keyboard-tap',
62
+ /** The user has pressed a virtual or software keyboard key. */
63
+ Keyboard_Press = 'keyboard-press',
64
+ /** The user has released a virtual keyboard key. */
65
+ Keyboard_Release = 'keyboard-release',
66
+ /** The user has performed a long press on an object that results in an action being performed. */
67
+ Long_Press = 'long-press',
68
+ /** The user has pressed on a virtual on-screen key. */
69
+ Virtual_Key = 'virtual-key',
70
+ /** The user has released a virtual key. */
71
+ Virtual_Key_Release = 'virtual-key-release',
72
+ /** No haptic feedback should be performed. */
73
+ No_Haptics = 'no-haptics',
74
+ /**
75
+ * The user is switching between a series of potential choices. For example, items in a list
76
+ * or discrete points on a slider.
77
+ */
78
+ Segment_Tick = 'segment-tick',
79
+ /**
80
+ * The user is switching between a series of many potential choices. For example, minutes on a
81
+ * clock face or individual percentages. Expected to be very soft, so as not to be uncomfortable
82
+ * when performed a lot in quick succession — if the device can't make a suitably soft
83
+ * vibration, it may not make any vibration at all.
84
+ */
85
+ Segment_Frequent_Tick = 'segment-frequent-tick',
86
+ /** The user has performed a selection/insertion handle move on a text field. */
87
+ Text_Handle_Move = 'text-handle-move',
88
+ }
@@ -0,0 +1,5 @@
1
+ // @symbiote-native/haptics/react: the React entry over the framework-agnostic core.
2
+ // Upstream ships free async functions and three enums, no per-instance state and no event
3
+ // stream (same shape as @symbiote-native/local-auth) — there is nothing for a hook to wrap, so
4
+ // this is a plain re-export.
5
+ export * from '../core';
@@ -0,0 +1,6 @@
1
+ // @symbiote-native/haptics/svelte: the Svelte entry over the framework-agnostic core.
2
+ // Upstream ships free async functions and three enums, no per-instance state and no event
3
+ // stream (same shape as @symbiote-native/local-auth) — there is nothing for a rune to wrap, so
4
+ // this is a plain re-export. Svelte's lifecycle bucket is `runes/` (adapters/svelte/src/runes —
5
+ // never React's `hooks` or Vue's `composables`), and this package has none to fill.
6
+ export * from '../core';
@@ -0,0 +1,4 @@
1
+ // @symbiote-native/haptics/vue: the Vue entry over the framework-agnostic core. Same reasoning
2
+ // as the React entry — no per-instance state or event stream to wire onto Vue's reactivity, so
3
+ // this is a plain re-export.
4
+ export * from '../core';