expo-orb 0.1.0 → 0.2.2

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 (41) hide show
  1. package/README.md +209 -1
  2. package/android/build.gradle +2 -0
  3. package/android/src/main/java/expo/modules/breathing/BreathingConfiguration.kt +25 -0
  4. package/android/src/main/java/expo/modules/breathing/BreathingExerciseView.kt +610 -0
  5. package/android/src/main/java/expo/modules/breathing/BreathingSharedState.kt +108 -0
  6. package/android/src/main/java/expo/modules/breathing/BreathingTextCue.kt +51 -0
  7. package/android/src/main/java/expo/modules/breathing/ExpoBreathingExerciseModule.kt +177 -0
  8. package/android/src/main/java/expo/modules/breathing/ExpoBreathingExerciseView.kt +144 -0
  9. package/android/src/main/java/expo/modules/breathing/MorphingBlobView.kt +128 -0
  10. package/android/src/main/java/expo/modules/breathing/ProgressRingView.kt +50 -0
  11. package/build/ExpoBreathingExercise.types.d.ts +48 -0
  12. package/build/ExpoBreathingExercise.types.d.ts.map +1 -0
  13. package/build/ExpoBreathingExercise.types.js +2 -0
  14. package/build/ExpoBreathingExercise.types.js.map +1 -0
  15. package/build/ExpoBreathingExerciseModule.d.ts +16 -0
  16. package/build/ExpoBreathingExerciseModule.d.ts.map +1 -0
  17. package/build/ExpoBreathingExerciseModule.js +52 -0
  18. package/build/ExpoBreathingExerciseModule.js.map +1 -0
  19. package/build/ExpoBreathingExerciseView.d.ts +4 -0
  20. package/build/ExpoBreathingExerciseView.d.ts.map +1 -0
  21. package/build/ExpoBreathingExerciseView.js +7 -0
  22. package/build/ExpoBreathingExerciseView.js.map +1 -0
  23. package/build/index.d.ts +3 -0
  24. package/build/index.d.ts.map +1 -1
  25. package/build/index.js +3 -0
  26. package/build/index.js.map +1 -1
  27. package/expo-module.config.json +2 -2
  28. package/ios/Breathing/BreathingConfiguration.swift +57 -0
  29. package/ios/Breathing/BreathingExerciseView.swift +451 -0
  30. package/ios/Breathing/BreathingParticlesView.swift +81 -0
  31. package/ios/Breathing/BreathingSharedState.swift +84 -0
  32. package/ios/Breathing/BreathingTextCue.swift +14 -0
  33. package/ios/Breathing/MorphingBlobView.swift +242 -0
  34. package/ios/Breathing/ProgressRingView.swift +27 -0
  35. package/ios/ExpoBreathingExerciseModule.swift +182 -0
  36. package/ios/ExpoBreathingExerciseView.swift +124 -0
  37. package/package.json +8 -5
  38. package/src/ExpoBreathingExercise.types.ts +50 -0
  39. package/src/ExpoBreathingExerciseModule.ts +67 -0
  40. package/src/ExpoBreathingExerciseView.tsx +11 -0
  41. package/src/index.ts +11 -0
package/README.md CHANGED
@@ -8,7 +8,7 @@ Inspired by and iOS implementation from [metasidd/Orb](https://github.com/metasi
8
8
 
9
9
  | iOS | Android |
10
10
  |:---:|:-------:|
11
- | ![iOS Demo](https://raw.githubusercontent.com/enso-works/expo-orb/main/docs/demo-ios.gif) | ![Android Demo](https://raw.githubusercontent.com/enso-works/expo-orb/main/docs/demo-android.gif) |
11
+ | ![iOS Demo](https://raw.githubusercontent.com/enso-works/expo-orb-animation/main/docs/demo-ios.gif) | ![Android Demo](https://raw.githubusercontent.com/enso-works/expo-orb-animation/main/docs/demo-android.gif) |
12
12
 
13
13
  ## Features
14
14
 
@@ -124,6 +124,214 @@ export default function App() {
124
124
  }
125
125
  ```
126
126
 
127
+ ---
128
+
129
+ ## Breathing Exercise Component
130
+
131
+ A guided breathing exercise component with morphing blob animation, progress ring, and text cues. Supports custom breathing patterns and built-in presets.
132
+
133
+ ### Breathing Demo
134
+
135
+ | iOS | Android |
136
+ |:---:|:-------:|
137
+ | ![iOS Breathing Demo](https://raw.githubusercontent.com/enso-works/expo-orb-animation/main/docs/breathing-ios.gif) | ![Android Breathing Demo](https://raw.githubusercontent.com/enso-works/expo-orb-animation/main/docs/breathing-android.gif) |
138
+
139
+ ### Basic Usage
140
+
141
+ ```tsx
142
+ import {
143
+ ExpoBreathingExerciseView,
144
+ startBreathingExercise,
145
+ stopBreathingExercise,
146
+ getBreathingPreset,
147
+ } from 'expo-orb';
148
+
149
+ function BreathingScreen() {
150
+ const handleStart = () => {
151
+ // Use a built-in preset
152
+ startBreathingExercise(getBreathingPreset('box'));
153
+ };
154
+
155
+ return (
156
+ <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
157
+ <ExpoBreathingExerciseView
158
+ style={{ width: 300, height: 300 }}
159
+ blobColors={['#7c3aed', '#3b82f6', '#ec4899']}
160
+ showProgressRing={true}
161
+ showTextCue={true}
162
+ onPhaseChange={(e) => console.log('Phase:', e.nativeEvent.label)}
163
+ onExerciseComplete={(e) => console.log('Done! Cycles:', e.nativeEvent.totalCycles)}
164
+ />
165
+ <Button title="Start" onPress={handleStart} />
166
+ <Button title="Stop" onPress={stopBreathingExercise} />
167
+ </View>
168
+ );
169
+ }
170
+ ```
171
+
172
+ ### Breathing Props
173
+
174
+ | Prop | Type | Default | Description |
175
+ |------|------|---------|-------------|
176
+ | `blobColors` | `ColorValue[]` | `['green', 'blue', 'pink']` | Gradient colors for the morphing blob |
177
+ | `innerBlobColor` | `ColorValue` | `'white'` | Color of the inner blob |
178
+ | `glowColor` | `ColorValue` | `'white'` | Color of the glow effects |
179
+ | `particleColor` | `ColorValue` | `'white'` | Color of floating particles |
180
+ | `progressRingColor` | `ColorValue` | `'white'` | Color of the progress ring |
181
+ | `textColor` | `ColorValue` | `'white'` | Color of the instruction text |
182
+ | `showProgressRing` | `boolean` | `true` | Show/hide the progress ring |
183
+ | `showTextCue` | `boolean` | `true` | Show/hide the instruction text (e.g., "Breathe In") |
184
+ | `showInnerBlob` | `boolean` | `true` | Show/hide the inner blob |
185
+ | `showShadow` | `boolean` | `true` | Show/hide drop shadow |
186
+ | `showParticles` | `boolean` | `true` | Show/hide floating particles |
187
+ | `showWavyBlobs` | `boolean` | `true` | Show/hide wavy blob overlays |
188
+ | `showGlowEffects` | `boolean` | `true` | Show/hide rotating glow effects |
189
+ | `pointCount` | `number` | `8` | Number of points for blob morphing |
190
+ | `wobbleIntensity` | `number` | `0.5` | Intensity of wobble animation (0-1) |
191
+ | `style` | `StyleProp<ViewStyle>` | - | Container style (set width/height here) |
192
+
193
+ ### Breathing Events
194
+
195
+ | Event | Payload | Description |
196
+ |-------|---------|-------------|
197
+ | `onPhaseChange` | `{ phase, label, phaseIndex, cycle }` | Fired when the breathing phase changes |
198
+ | `onExerciseComplete` | `{ totalCycles, totalDuration }` | Fired when all cycles complete |
199
+
200
+ ### Breathing Control Functions
201
+
202
+ ```tsx
203
+ import {
204
+ startBreathingExercise,
205
+ stopBreathingExercise,
206
+ pauseBreathingExercise,
207
+ resumeBreathingExercise,
208
+ getBreathingPreset,
209
+ } from 'expo-orb';
210
+ ```
211
+
212
+ | Function | Description |
213
+ |----------|-------------|
214
+ | `startBreathingExercise(pattern)` | Start the exercise with a custom pattern or preset |
215
+ | `stopBreathingExercise()` | Stop the current exercise |
216
+ | `pauseBreathingExercise()` | Pause the current exercise |
217
+ | `resumeBreathingExercise()` | Resume a paused exercise |
218
+ | `getBreathingPreset(name)` | Get a built-in preset by name |
219
+
220
+ ### Built-in Presets
221
+
222
+ | Preset | Pattern | Cycles | Description |
223
+ |--------|---------|--------|-------------|
224
+ | `'relaxing'` | 4s in, 7s hold, 8s out | 4 | 4-7-8 technique for relaxation |
225
+ | `'box'` | 4s in, 4s hold, 4s out, 4s hold | 4 | Box breathing for focus |
226
+ | `'energizing'` | 2s in, 2s out | 10 | Quick breathing for energy |
227
+ | `'calming'` | 4s in, 6s out | 6 | Extended exhale for calm |
228
+
229
+ ### Custom Breathing Patterns
230
+
231
+ Create your own breathing patterns:
232
+
233
+ ```tsx
234
+ import { startBreathingExercise, BreathingPattern } from 'expo-orb';
235
+
236
+ const customPattern: BreathingPattern = {
237
+ phases: [
238
+ { phase: 'inhale', duration: 5000, targetScale: 1.35, label: 'Breathe In' },
239
+ { phase: 'holdIn', duration: 3000, targetScale: 1.35, label: 'Hold' },
240
+ { phase: 'exhale', duration: 7000, targetScale: 0.75, label: 'Breathe Out' },
241
+ { phase: 'holdOut', duration: 2000, targetScale: 0.75, label: 'Rest' },
242
+ ],
243
+ cycles: 5, // undefined for infinite
244
+ };
245
+
246
+ startBreathingExercise(customPattern);
247
+ ```
248
+
249
+ ### Phase Types
250
+
251
+ | Phase | Description |
252
+ |-------|-------------|
253
+ | `'inhale'` | Breathing in - blob expands |
254
+ | `'holdIn'` | Holding breath after inhale |
255
+ | `'exhale'` | Breathing out - blob contracts |
256
+ | `'holdOut'` | Holding breath after exhale |
257
+
258
+ ### Complete Breathing Example
259
+
260
+ ```tsx
261
+ import * as React from 'react';
262
+ import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
263
+ import {
264
+ ExpoBreathingExerciseView,
265
+ startBreathingExercise,
266
+ stopBreathingExercise,
267
+ pauseBreathingExercise,
268
+ resumeBreathingExercise,
269
+ getBreathingPreset,
270
+ BreathingPreset,
271
+ } from 'expo-orb';
272
+
273
+ export default function BreathingApp() {
274
+ const [currentPhase, setCurrentPhase] = React.useState('');
275
+ const [isPaused, setIsPaused] = React.useState(false);
276
+
277
+ const startPreset = (preset: BreathingPreset) => {
278
+ setIsPaused(false);
279
+ startBreathingExercise(getBreathingPreset(preset));
280
+ };
281
+
282
+ return (
283
+ <View style={styles.container}>
284
+ <ExpoBreathingExerciseView
285
+ style={styles.breathingView}
286
+ blobColors={['#667eea', '#764ba2', '#f093fb']}
287
+ progressRingColor="#ffffff"
288
+ textColor="#ffffff"
289
+ showProgressRing={true}
290
+ showTextCue={true}
291
+ showParticles={true}
292
+ onPhaseChange={(e) => setCurrentPhase(e.nativeEvent.label)}
293
+ onExerciseComplete={() => setCurrentPhase('Complete!')}
294
+ />
295
+
296
+ <View style={styles.presets}>
297
+ <TouchableOpacity onPress={() => startPreset('box')}>
298
+ <Text>Box</Text>
299
+ </TouchableOpacity>
300
+ <TouchableOpacity onPress={() => startPreset('relaxing')}>
301
+ <Text>Relaxing</Text>
302
+ </TouchableOpacity>
303
+ <TouchableOpacity onPress={() => startPreset('calming')}>
304
+ <Text>Calming</Text>
305
+ </TouchableOpacity>
306
+ </View>
307
+
308
+ <View style={styles.controls}>
309
+ <TouchableOpacity onPress={() => {
310
+ if (isPaused) {
311
+ resumeBreathingExercise();
312
+ } else {
313
+ pauseBreathingExercise();
314
+ }
315
+ setIsPaused(!isPaused);
316
+ }}>
317
+ <Text>{isPaused ? 'Resume' : 'Pause'}</Text>
318
+ </TouchableOpacity>
319
+ <TouchableOpacity onPress={stopBreathingExercise}>
320
+ <Text>Stop</Text>
321
+ </TouchableOpacity>
322
+ </View>
323
+ </View>
324
+ );
325
+ }
326
+
327
+ const styles = StyleSheet.create({
328
+ container: { flex: 1, alignItems: 'center', justifyContent: 'center' },
329
+ breathingView: { width: 280, height: 280 },
330
+ presets: { flexDirection: 'row', gap: 16, marginTop: 32 },
331
+ controls: { flexDirection: 'row', gap: 24, marginTop: 16 },
332
+ });
333
+ ```
334
+
127
335
  ## License
128
336
 
129
337
  MIT
@@ -56,5 +56,7 @@ dependencies {
56
56
  implementation 'androidx.compose.ui:ui-graphics'
57
57
  implementation 'androidx.compose.foundation:foundation'
58
58
  implementation 'androidx.compose.runtime:runtime'
59
+ implementation 'androidx.compose.material3:material3'
60
+ implementation 'androidx.compose.animation:animation'
59
61
  implementation 'androidx.activity:activity-compose:1.8.2'
60
62
  }
@@ -0,0 +1,25 @@
1
+ package expo.modules.breathing
2
+
3
+ import androidx.compose.ui.graphics.Color
4
+
5
+ data class BreathingConfiguration(
6
+ val blobColors: List<Color> = listOf(
7
+ Color(0xFF66B3E6), // Light blue
8
+ Color(0xFF4D80CC), // Medium blue
9
+ Color(0xFF804DB3) // Purple
10
+ ),
11
+ val innerBlobColor: Color = Color.White.copy(alpha = 0.3f),
12
+ val glowColor: Color = Color.White,
13
+ val particleColor: Color = Color.White,
14
+ val progressRingColor: Color = Color.White.copy(alpha = 0.5f),
15
+ val textColor: Color = Color.White,
16
+ val showProgressRing: Boolean = true,
17
+ val showTextCue: Boolean = true,
18
+ val showInnerBlob: Boolean = true,
19
+ val showShadow: Boolean = true,
20
+ val showParticles: Boolean = true,
21
+ val showWavyBlobs: Boolean = true,
22
+ val showGlowEffects: Boolean = true,
23
+ val pointCount: Int = 8,
24
+ val wobbleIntensity: Double = 1.0
25
+ )