@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 +1 -1
- package/src/config/ConfigProvider.tsx +32 -4
- package/src/theme/ThemeProvider.tsx +31 -0
- package/src/theme/animations.ts +9 -7
- package/src/utils/expoGo.ts +72 -0
- package/src/utils/index.ts +5 -0
package/package.json
CHANGED
|
@@ -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
|
-
*
|
|
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
|
|
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(
|
|
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
|
}, []);
|
package/src/theme/animations.ts
CHANGED
|
@@ -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?:
|
|
26
|
+
easing?: EasingFn;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
const
|
|
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
|
+
}
|