@metacells/mcellui-core 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metacells/mcellui-core",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Core theme system and utilities for mcellui - React Native UI components",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -6,9 +6,20 @@
6
6
  *
7
7
  * @example
8
8
  * ```tsx
9
- * // App.tsx
9
+ * // App.tsx - with auto-config (requires metro-plugin)
10
10
  * import { ConfigProvider } from '@nativeui/core';
11
- * import config from './nativeui.config';
11
+ *
12
+ * export default function App() {
13
+ * return (
14
+ * <ConfigProvider>
15
+ * <YourApp />
16
+ * </ConfigProvider>
17
+ * );
18
+ * }
19
+ *
20
+ * // App.tsx - with explicit config
21
+ * import { ConfigProvider } from '@nativeui/core';
22
+ * import config from './mcellui.config';
12
23
  *
13
24
  * export default function App() {
14
25
  * return (
@@ -25,6 +36,17 @@ import { ThemeProvider, ThemeProviderProps } from '../theme/ThemeProvider';
25
36
  import { NativeUIConfig, ResolvedNativeUIConfig } from './types';
26
37
  import { resolveConfig } from './defineConfig';
27
38
 
39
+ // Try to load auto-config from metro-plugin (if configured)
40
+ let autoConfig: NativeUIConfig = {};
41
+ try {
42
+ // This module is resolved by @metacells/mcellui-metro-plugin
43
+ // to either the user's config file or an empty default
44
+ autoConfig = require('@mcellui/auto-config').default ?? {};
45
+ } catch {
46
+ // Metro plugin not configured, use empty default
47
+ autoConfig = {};
48
+ }
49
+
28
50
  // Config context for accessing raw config values
29
51
  const ConfigContext = createContext<ResolvedNativeUIConfig | null>(null);
30
52
 
@@ -60,8 +82,11 @@ export interface ConfigProviderProps {
60
82
  }
61
83
 
62
84
  /**
63
- * ConfigProvider combines your nativeui.config.ts with ThemeProvider.
85
+ * ConfigProvider combines your mcellui.config.ts with ThemeProvider.
64
86
  * Use this as the root of your app for the easiest setup.
87
+ *
88
+ * If no config is provided and @metacells/mcellui-metro-plugin is set up,
89
+ * it will automatically load your mcellui.config.ts file.
65
90
  */
66
91
  export function ConfigProvider({
67
92
  config,
@@ -70,8 +95,11 @@ export function ConfigProvider({
70
95
  radius,
71
96
  children,
72
97
  }: ConfigProviderProps) {
98
+ // Use provided config, or fall back to auto-discovered config
99
+ const effectiveConfig = config ?? autoConfig;
100
+
73
101
  // Resolve config with defaults
74
- const resolvedConfig = useMemo(() => resolveConfig(config), [config]);
102
+ const resolvedConfig = useMemo(() => resolveConfig(effectiveConfig), [effectiveConfig]);
75
103
 
76
104
  // Props can override config values
77
105
  const finalTheme = theme ?? resolvedConfig.theme;
@@ -24,6 +24,7 @@ import React, { createContext, useContext, useMemo, useState, useCallback, useEf
24
24
  import { useColorScheme, ViewStyle } from 'react-native';
25
25
  import { lightColors, darkColors, ThemeColors } from './colors';
26
26
  import { setHapticsEnabled } from '../utils/haptics';
27
+ import { setAnimationsDisabled, isExpoGo } from '../utils/expoGo';
27
28
  import { spacing } from './spacing';
28
29
  import {
29
30
  createRadius,
@@ -251,6 +252,24 @@ export interface ThemeProviderProps {
251
252
  */
252
253
  animationPreset?: AnimationPreset;
253
254
 
255
+ /**
256
+ * Enable or disable animations globally.
257
+ * - `true`: Force enable animations
258
+ * - `false`: Force disable animations (components will use static styles)
259
+ * - `'auto'`: Automatically detect - disable in Expo Go, enable in dev/prod builds
260
+ * @default 'auto'
261
+ *
262
+ * @example
263
+ * ```tsx
264
+ * // Auto-detect (disabled in Expo Go)
265
+ * <ThemeProvider animations="auto">
266
+ *
267
+ * // Force disable for testing or accessibility
268
+ * <ThemeProvider animations={false}>
269
+ * ```
270
+ */
271
+ animations?: boolean | 'auto';
272
+
254
273
  /** Children components */
255
274
  children: ReactNode;
256
275
  }
@@ -265,6 +284,7 @@ export function ThemeProvider({
265
284
  darkColors: darkColorOverrides,
266
285
  haptics = true,
267
286
  animationPreset = defaultAnimationPreset,
287
+ animations = 'auto',
268
288
  children,
269
289
  }: ThemeProviderProps) {
270
290
  const systemColorScheme = useColorScheme();
@@ -275,6 +295,17 @@ export function ThemeProvider({
275
295
  setHapticsEnabled(haptics);
276
296
  }, [haptics]);
277
297
 
298
+ // Set global animations enabled state
299
+ useEffect(() => {
300
+ if (animations === 'auto') {
301
+ // Use automatic detection (Expo Go = disabled)
302
+ setAnimationsDisabled(null);
303
+ } else {
304
+ // Use explicit override
305
+ setAnimationsDisabled(!animations);
306
+ }
307
+ }, [animations]);
308
+
278
309
  const setColorScheme = useCallback((newPreference: ColorSchemePreference) => {
279
310
  setPreference(newPreference);
280
311
  }, []);
@@ -7,8 +7,6 @@
7
7
  * Inspired by iOS 17+ animations and Linear App micro-interactions.
8
8
  */
9
9
 
10
- import { Easing, EasingFunction } from 'react-native-reanimated';
11
-
12
10
  // Type definitions for Reanimated-compatible configs
13
11
  export interface SpringConfig {
14
12
  damping?: number;
@@ -20,15 +18,19 @@ export interface SpringConfig {
20
18
  velocity?: number;
21
19
  }
22
20
 
21
+ // Generic easing function type that works with or without Reanimated
22
+ type EasingFn = (t: number) => number;
23
+
23
24
  export interface TimingConfig {
24
25
  duration?: number;
25
- easing?: EasingFunction;
26
+ easing?: EasingFn;
26
27
  }
27
28
 
28
- // Common easing functions using Reanimated's Easing module (worklet-compatible)
29
- const easeOutQuad = Easing.out(Easing.quad);
30
- const easeOutCubic = Easing.out(Easing.cubic);
31
- const easeInQuad = Easing.in(Easing.quad);
29
+ // Simple easing functions that don't require Reanimated
30
+ // These are compatible with both Reanimated and standard Animated API
31
+ const easeOutQuad: EasingFn = (t) => t * (2 - t);
32
+ const easeOutCubic: EasingFn = (t) => 1 - Math.pow(1 - t, 3);
33
+ const easeInQuad: EasingFn = (t) => t * t;
32
34
 
33
35
  /**
34
36
  * Spring configurations for different animation contexts
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Expo Go Detection Utility
3
+ *
4
+ * Provides detection for Expo Go environment to gracefully disable
5
+ * Reanimated animations when running in Expo Go client.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * import { isExpoGo, areAnimationsDisabled } from '@nativeui/core';
10
+ *
11
+ * // Check environment
12
+ * if (isExpoGo()) {
13
+ * console.log('Running in Expo Go');
14
+ * }
15
+ *
16
+ * // Check if animations should be disabled
17
+ * if (areAnimationsDisabled()) {
18
+ * // Skip animation, use static styles
19
+ * }
20
+ * ```
21
+ */
22
+
23
+ let executionEnvironment: string | undefined;
24
+
25
+ // Try to load expo-constants at module load time
26
+ try {
27
+ // Dynamic require to avoid bundler issues when expo-constants is not available
28
+ const Constants = require('expo-constants').default;
29
+ executionEnvironment = Constants?.executionEnvironment;
30
+ } catch {
31
+ // expo-constants not available - not in Expo environment
32
+ executionEnvironment = undefined;
33
+ }
34
+
35
+ /**
36
+ * Check if the app is running in Expo Go client.
37
+ * Returns false in development builds, production builds, or non-Expo environments.
38
+ */
39
+ export function isExpoGo(): boolean {
40
+ return executionEnvironment === 'storeClient';
41
+ }
42
+
43
+ // Internal state for animation override
44
+ let animationsDisabledOverride: boolean | null = null;
45
+
46
+ /**
47
+ * Manually override the animation disabled state.
48
+ * Set to `true` to force disable animations, `false` to force enable,
49
+ * or `null` to use automatic detection (Expo Go = disabled).
50
+ *
51
+ * @param disabled - Override value or null for automatic
52
+ */
53
+ export function setAnimationsDisabled(disabled: boolean | null): void {
54
+ animationsDisabledOverride = disabled;
55
+ }
56
+
57
+ /**
58
+ * Check if animations should be disabled.
59
+ * Returns true if:
60
+ * - Manually overridden to true via setAnimationsDisabled(true)
61
+ * - Running in Expo Go (automatic detection)
62
+ *
63
+ * Returns false if:
64
+ * - Manually overridden to false via setAnimationsDisabled(false)
65
+ * - Running in development/production build
66
+ */
67
+ export function areAnimationsDisabled(): boolean {
68
+ if (animationsDisabledOverride !== null) {
69
+ return animationsDisabledOverride;
70
+ }
71
+ return isExpoGo();
72
+ }
@@ -26,3 +26,8 @@ export {
26
26
  hapticPresets,
27
27
  type HapticStyle,
28
28
  } from './haptics';
29
+ export {
30
+ isExpoGo,
31
+ setAnimationsDisabled,
32
+ areAnimationsDisabled,
33
+ } from './expoGo';