@metacells/mcellui-core 0.1.2 → 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 (46) hide show
  1. package/dist/index-DtYPPizh.d.ts +682 -0
  2. package/dist/index.d.ts +3851 -0
  3. package/dist/index.js +7191 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/shadows-CwlbfJco.d.ts +194 -0
  6. package/dist/tokens/index.d.ts +17 -0
  7. package/dist/tokens/index.js +176 -0
  8. package/dist/tokens/index.js.map +1 -0
  9. package/dist/utils/index.d.ts +2 -0
  10. package/dist/utils/index.js +4150 -0
  11. package/dist/utils/index.js.map +1 -0
  12. package/package.json +18 -11
  13. package/src/components/ErrorBoundary.tsx +0 -188
  14. package/src/components/index.ts +0 -6
  15. package/src/config/ConfigProvider.tsx +0 -145
  16. package/src/config/defineConfig.ts +0 -75
  17. package/src/config/index.ts +0 -42
  18. package/src/config/types.ts +0 -185
  19. package/src/constants.ts +0 -203
  20. package/src/index.ts +0 -144
  21. package/src/primitives/Portal.tsx +0 -185
  22. package/src/primitives/Pressable.tsx +0 -114
  23. package/src/primitives/Slot.tsx +0 -177
  24. package/src/primitives/index.ts +0 -19
  25. package/src/theme/ThemeProvider.tsx +0 -506
  26. package/src/theme/animations.ts +0 -381
  27. package/src/theme/colors.ts +0 -266
  28. package/src/theme/components.ts +0 -267
  29. package/src/theme/index.ts +0 -130
  30. package/src/theme/presets.ts +0 -341
  31. package/src/theme/radius.ts +0 -175
  32. package/src/theme/shadows.ts +0 -166
  33. package/src/theme/spacing.ts +0 -41
  34. package/src/theme/typography.ts +0 -389
  35. package/src/tokens/colors.ts +0 -67
  36. package/src/tokens/index.ts +0 -29
  37. package/src/tokens/radius.ts +0 -18
  38. package/src/tokens/shadows.ts +0 -38
  39. package/src/tokens/spacing.ts +0 -45
  40. package/src/tokens/typography.ts +0 -70
  41. package/src/utils/accessibility.ts +0 -112
  42. package/src/utils/cn.ts +0 -58
  43. package/src/utils/expoGo.ts +0 -72
  44. package/src/utils/haptics.ts +0 -125
  45. package/src/utils/index.ts +0 -33
  46. package/src/utils/platform.ts +0 -66
@@ -1,112 +0,0 @@
1
- /**
2
- * Accessibility Utilities
3
- *
4
- * Helpers for building accessible components
5
- */
6
-
7
- import { AccessibilityRole, AccessibilityState } from 'react-native';
8
-
9
- export interface A11yProps {
10
- accessible?: boolean;
11
- accessibilityLabel?: string;
12
- accessibilityHint?: string;
13
- accessibilityRole?: AccessibilityRole;
14
- accessibilityState?: AccessibilityState;
15
- }
16
-
17
- /**
18
- * Creates accessibility props for a button
19
- */
20
- export function buttonA11y(
21
- label: string,
22
- options?: {
23
- hint?: string;
24
- disabled?: boolean;
25
- selected?: boolean;
26
- }
27
- ): A11yProps {
28
- return {
29
- accessible: true,
30
- accessibilityRole: 'button',
31
- accessibilityLabel: label,
32
- accessibilityHint: options?.hint,
33
- accessibilityState: {
34
- disabled: options?.disabled,
35
- selected: options?.selected,
36
- },
37
- };
38
- }
39
-
40
- /**
41
- * Creates accessibility props for a checkbox/switch
42
- */
43
- export function toggleA11y(
44
- label: string,
45
- checked: boolean,
46
- options?: {
47
- hint?: string;
48
- disabled?: boolean;
49
- }
50
- ): A11yProps {
51
- return {
52
- accessible: true,
53
- accessibilityRole: 'switch',
54
- accessibilityLabel: label,
55
- accessibilityHint: options?.hint,
56
- accessibilityState: {
57
- checked,
58
- disabled: options?.disabled,
59
- },
60
- };
61
- }
62
-
63
- /**
64
- * Creates accessibility props for a link
65
- */
66
- export function linkA11y(
67
- label: string,
68
- options?: {
69
- hint?: string;
70
- }
71
- ): A11yProps {
72
- return {
73
- accessible: true,
74
- accessibilityRole: 'link',
75
- accessibilityLabel: label,
76
- accessibilityHint: options?.hint ?? 'Double tap to open',
77
- };
78
- }
79
-
80
- /**
81
- * Creates accessibility props for an image
82
- */
83
- export function imageA11y(
84
- label: string,
85
- options?: {
86
- decorative?: boolean;
87
- }
88
- ): A11yProps {
89
- if (options?.decorative) {
90
- return {
91
- accessible: false,
92
- accessibilityLabel: undefined,
93
- };
94
- }
95
-
96
- return {
97
- accessible: true,
98
- accessibilityRole: 'image',
99
- accessibilityLabel: label,
100
- };
101
- }
102
-
103
- /**
104
- * Creates accessibility props for a heading
105
- */
106
- export function headingA11y(label: string): A11yProps {
107
- return {
108
- accessible: true,
109
- accessibilityRole: 'header',
110
- accessibilityLabel: label,
111
- };
112
- }
package/src/utils/cn.ts DELETED
@@ -1,58 +0,0 @@
1
- /**
2
- * cn() - Class Name / Style Merger
3
- *
4
- * The core utility for merging styles in mcellui.
5
- * Inspired by shadcn/ui's cn() but adapted for React Native StyleSheet.
6
- */
7
-
8
- import { StyleSheet, ViewStyle, TextStyle, ImageStyle } from 'react-native';
9
-
10
- type Style = ViewStyle | TextStyle | ImageStyle;
11
- type StyleInput = Style | Style[] | null | undefined | false;
12
-
13
- /**
14
- * Merges multiple style objects into a single flattened style.
15
- * Handles undefined, null, false, and nested arrays.
16
- *
17
- * @example
18
- * ```tsx
19
- * const styles = cn(
20
- * baseStyles.container,
21
- * isActive && activeStyles,
22
- * { padding: 16 }
23
- * );
24
- * ```
25
- */
26
- export function cn(...inputs: StyleInput[]): Style {
27
- const styles: Style[] = [];
28
-
29
- for (const input of inputs) {
30
- if (!input) continue;
31
-
32
- if (Array.isArray(input)) {
33
- const flattened = StyleSheet.flatten(input);
34
- if (flattened) styles.push(flattened);
35
- } else {
36
- styles.push(input);
37
- }
38
- }
39
-
40
- return StyleSheet.flatten(styles);
41
- }
42
-
43
- /**
44
- * Creates a style object from conditional styles.
45
- * More explicit alternative to cn() for complex conditions.
46
- *
47
- * @example
48
- * ```tsx
49
- * const styles = mergeStyles({
50
- * base: baseStyle,
51
- * active: isActive && activeStyle,
52
- * disabled: isDisabled && disabledStyle,
53
- * });
54
- * ```
55
- */
56
- export function mergeStyles(styles: Record<string, StyleInput>): Style {
57
- return cn(...Object.values(styles));
58
- }
@@ -1,72 +0,0 @@
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 '@metacells/mcellui-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
- }
@@ -1,125 +0,0 @@
1
- /**
2
- * Haptics Utilities
3
- *
4
- * Unified haptic feedback interface.
5
- * Falls back gracefully when expo-haptics is not available.
6
- *
7
- * Design Philosophy:
8
- * - Every interactive element should provide tactile feedback
9
- * - Haptics should be subtle and purposeful
10
- * - Respect user's haptic preferences
11
- */
12
-
13
- export type HapticStyle = 'light' | 'medium' | 'heavy' | 'success' | 'warning' | 'error' | 'selection';
14
-
15
- /**
16
- * Haptic presets for common interactions
17
- */
18
- export const hapticPresets = {
19
- /** For button presses, checkbox toggles */
20
- buttonPress: 'light' as HapticStyle,
21
- /** For switch toggles */
22
- toggle: 'medium' as HapticStyle,
23
- /** For successful actions */
24
- success: 'success' as HapticStyle,
25
- /** For errors */
26
- error: 'error' as HapticStyle,
27
- /** For selection changes (radio, picker) */
28
- selection: 'selection' as HapticStyle,
29
- /** For destructive actions */
30
- destructive: 'warning' as HapticStyle,
31
- } as const;
32
-
33
- let Haptics: typeof import('expo-haptics') | null = null;
34
-
35
- // Try to load expo-haptics (optional dependency)
36
- try {
37
- Haptics = require('expo-haptics');
38
- } catch {
39
- // expo-haptics not available, haptics will be no-ops
40
- }
41
-
42
- // ─────────────────────────────────────────────────────────────────────────────
43
- // Global Haptics State
44
- // ─────────────────────────────────────────────────────────────────────────────
45
-
46
- let hapticsEnabled = true;
47
-
48
- /**
49
- * Enable or disable haptic feedback globally.
50
- * Use this to respect user preferences or accessibility settings.
51
- *
52
- * @example
53
- * ```tsx
54
- * // In ThemeProvider or app setup
55
- * setHapticsEnabled(false);
56
- *
57
- * // Or via config
58
- * <ThemeProvider haptics={false}>
59
- * ```
60
- */
61
- export function setHapticsEnabled(enabled: boolean): void {
62
- hapticsEnabled = enabled;
63
- }
64
-
65
- /**
66
- * Check if haptics are currently enabled
67
- */
68
- export function isHapticsEnabled(): boolean {
69
- return hapticsEnabled && Haptics !== null;
70
- }
71
-
72
- /**
73
- * Trigger haptic feedback
74
- *
75
- * Respects the global haptics enabled state. Disabled via:
76
- * - `setHapticsEnabled(false)`
77
- * - `<ThemeProvider haptics={false}>`
78
- * - `mcellui.config.ts` with `haptics: false`
79
- *
80
- * @example
81
- * ```tsx
82
- * onPress={() => {
83
- * haptic('light');
84
- * // ... rest of handler
85
- * }}
86
- * ```
87
- */
88
- export async function haptic(style: HapticStyle = 'light'): Promise<void> {
89
- if (!hapticsEnabled || !Haptics) return;
90
-
91
- try {
92
- switch (style) {
93
- case 'light':
94
- await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
95
- break;
96
- case 'medium':
97
- await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
98
- break;
99
- case 'heavy':
100
- await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy);
101
- break;
102
- case 'success':
103
- await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
104
- break;
105
- case 'warning':
106
- await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Warning);
107
- break;
108
- case 'error':
109
- await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
110
- break;
111
- case 'selection':
112
- await Haptics.selectionAsync();
113
- break;
114
- }
115
- } catch {
116
- // Silently fail if haptics not supported
117
- }
118
- }
119
-
120
- /**
121
- * Check if haptics are available
122
- */
123
- export function hapticsAvailable(): boolean {
124
- return Haptics !== null;
125
- }
@@ -1,33 +0,0 @@
1
- export { cn, mergeStyles } from './cn';
2
- export {
3
- isIOS,
4
- isAndroid,
5
- isWeb,
6
- iosVersion,
7
- androidApiLevel,
8
- isTablet,
9
- pixelRatio,
10
- roundToNearestPixel,
11
- getFontScale,
12
- } from './platform';
13
- export {
14
- buttonA11y,
15
- toggleA11y,
16
- linkA11y,
17
- imageA11y,
18
- headingA11y,
19
- type A11yProps,
20
- } from './accessibility';
21
- export {
22
- haptic,
23
- hapticsAvailable,
24
- setHapticsEnabled,
25
- isHapticsEnabled,
26
- hapticPresets,
27
- type HapticStyle,
28
- } from './haptics';
29
- export {
30
- isExpoGo,
31
- setAnimationsDisabled,
32
- areAnimationsDisabled,
33
- } from './expoGo';
@@ -1,66 +0,0 @@
1
- /**
2
- * Platform Utilities
3
- *
4
- * Helpers for platform-specific behavior
5
- */
6
-
7
- import { Platform, Dimensions, PixelRatio } from 'react-native';
8
-
9
- /**
10
- * Check if running on iOS
11
- */
12
- export const isIOS = Platform.OS === 'ios';
13
-
14
- /**
15
- * Check if running on Android
16
- */
17
- export const isAndroid = Platform.OS === 'android';
18
-
19
- /**
20
- * Check if running on web
21
- */
22
- export const isWeb = Platform.OS === 'web';
23
-
24
- /**
25
- * Get iOS version (returns 0 on non-iOS)
26
- */
27
- export const iosVersion = isIOS
28
- ? parseInt(String(Platform.Version), 10)
29
- : 0;
30
-
31
- /**
32
- * Get Android API level (returns 0 on non-Android)
33
- */
34
- export const androidApiLevel = isAndroid
35
- ? (Platform.Version as number)
36
- : 0;
37
-
38
- /**
39
- * Check if device is a tablet (rough heuristic)
40
- */
41
- export function isTablet(): boolean {
42
- const { width, height } = Dimensions.get('window');
43
- const aspectRatio = Math.max(width, height) / Math.min(width, height);
44
- const isLargeScreen = Math.min(width, height) >= 600;
45
-
46
- return isLargeScreen && aspectRatio < 1.6;
47
- }
48
-
49
- /**
50
- * Get pixel ratio for crisp rendering
51
- */
52
- export const pixelRatio = PixelRatio.get();
53
-
54
- /**
55
- * Round to nearest pixel for crisp lines
56
- */
57
- export function roundToNearestPixel(value: number): number {
58
- return PixelRatio.roundToNearestPixel(value);
59
- }
60
-
61
- /**
62
- * Get safe font scale (clamped for accessibility)
63
- */
64
- export function getFontScale(maxScale = 1.3): number {
65
- return Math.min(PixelRatio.getFontScale(), maxScale);
66
- }