@umituz/react-native-design-system 4.25.42 → 4.25.44

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 (33) hide show
  1. package/package.json +1 -1
  2. package/src/layouts/ScreenLayout/ScreenLayout.tsx +43 -22
  3. package/src/layouts/ScreenLayout/styles/screenLayoutStyles.ts +1 -1
  4. package/src/molecules/StepHeader/StepHeader.tsx +1 -1
  5. package/src/molecules/alerts/hooks/useAlertDismissHandler.ts +1 -1
  6. package/src/molecules/bottom-sheet/components/filter/FilterBottomSheet.tsx +1 -1
  7. package/src/molecules/navigation/TabsNavigator.tsx +1 -1
  8. package/src/molecules/navigation/hooks/useTabConfig.ts +2 -2
  9. package/src/molecules/splash/components/SplashScreen.tsx +1 -1
  10. package/src/onboarding/presentation/components/BackgroundVideo.tsx +0 -1
  11. package/src/responsive/padding/paddingUtils.ts +2 -3
  12. package/src/safe-area/index.ts +15 -18
  13. package/src/storage/presentation/hooks/useStore.ts +1 -8
  14. package/src/tanstack/presentation/hooks/usePrefetch.ts +2 -1
  15. package/src/theme/infrastructure/providers/DesignSystemProvider.tsx +11 -1
  16. package/src/typography/domain/entities/TypographyTypes.ts +1 -9
  17. package/src/typography/presentation/utils/textColorUtils.ts +1 -7
  18. package/src/utils/hooks/useAsyncOperation.ts +1 -1
  19. package/src/safe-area/__tests__/components/SafeAreaProvider.test.tsx +0 -15
  20. package/src/safe-area/__tests__/hooks/useContentSafeAreaPadding.test.tsx +0 -15
  21. package/src/safe-area/__tests__/hooks/useHeaderSafeAreaPadding.test.tsx +0 -15
  22. package/src/safe-area/__tests__/hooks/useSafeAreaInsets.test.tsx +0 -15
  23. package/src/safe-area/__tests__/hooks/useStatusBarSafeAreaPadding.test.tsx +0 -15
  24. package/src/safe-area/__tests__/integration/completeFlow.test.tsx +0 -26
  25. package/src/safe-area/__tests__/setup.ts +0 -50
  26. package/src/safe-area/__tests__/utils/performance.test.tsx +0 -18
  27. package/src/safe-area/__tests__/utils/testUtils.tsx +0 -44
  28. package/src/safe-area/components/SafeAreaProvider.tsx +0 -46
  29. package/src/safe-area/hooks/useContentSafeAreaPadding.ts +0 -41
  30. package/src/safe-area/hooks/useHeaderSafeAreaPadding.ts +0 -35
  31. package/src/safe-area/hooks/useStatusBarSafeAreaPadding.ts +0 -36
  32. package/src/safe-area/utils/optimization.ts +0 -49
  33. package/src/safe-area/utils/validation.ts +0 -50
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-design-system",
3
- "version": "4.25.42",
3
+ "version": "4.25.44",
4
4
  "description": "Universal design system for React Native apps - Consolidated package with atoms, molecules, organisms, theme, typography, responsive, safe area, exception, infinite scroll, UUID, image, timezone, offline, onboarding, and loading utilities",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -5,28 +5,29 @@
5
5
 
6
6
  import React, { useMemo } from 'react';
7
7
  import { View, ScrollView } from 'react-native';
8
- import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
8
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
9
9
  import { useAppDesignTokens } from '../../theme';
10
10
  import { getScreenLayoutConfig } from '../../responsive/responsiveLayout';
11
11
  import { AtomicKeyboardAvoidingView } from '../../atoms';
12
12
  import { getScreenLayoutStyles } from './styles/screenLayoutStyles';
13
13
  import type { ScreenLayoutProps } from './types';
14
14
 
15
- export const ScreenLayout: React.FC<ScreenLayoutProps> = ({
16
- children,
17
- scrollable = true,
18
- edges = ['top'],
19
- header,
20
- footer,
21
- backgroundColor,
22
- containerStyle,
23
- contentContainerStyle,
24
- testID,
25
- hideScrollIndicator = false,
26
- keyboardAvoiding = false,
27
- maxWidth,
28
- refreshControl,
29
- }) => {
15
+ export const ScreenLayout: React.FC<ScreenLayoutProps> = (props: ScreenLayoutProps) => {
16
+ const {
17
+ children,
18
+ scrollable = true,
19
+ edges = ['top', 'bottom', 'left', 'right'],
20
+ header,
21
+ footer,
22
+ backgroundColor,
23
+ containerStyle,
24
+ contentContainerStyle,
25
+ testID,
26
+ hideScrollIndicator = false,
27
+ keyboardAvoiding = false,
28
+ maxWidth,
29
+ refreshControl,
30
+ } = props;
30
31
  const tokens = useAppDesignTokens();
31
32
  const insets = useSafeAreaInsets();
32
33
 
@@ -49,13 +50,30 @@ export const ScreenLayout: React.FC<ScreenLayoutProps> = ({
49
50
 
50
51
  const bgColor = backgroundColor || tokens.colors.backgroundPrimary;
51
52
 
53
+ // Robust safe area handling
54
+ const paddingTop = edges.includes('top') ? insets.top : 0;
55
+ const paddingBottom = edges.includes('bottom') ? insets.bottom : 0;
56
+ const paddingLeft = edges.includes('left') ? insets.left : 0;
57
+ const paddingRight = edges.includes('right') ? insets.right : 0;
58
+
52
59
  const content = (
53
- <View style={styles.responsiveWrapper}>
60
+ <View style={[
61
+ styles.responsiveWrapper,
62
+ {
63
+ paddingTop,
64
+ paddingBottom: footer ? 0 : paddingBottom,
65
+ paddingLeft,
66
+ paddingRight,
67
+ }
68
+ ]}>
54
69
  {header}
55
70
  {scrollable ? (
56
71
  <ScrollView
57
72
  style={styles.scrollView}
58
- contentContainerStyle={[styles.scrollContent, contentContainerStyle]}
73
+ contentContainerStyle={[
74
+ styles.scrollContent,
75
+ contentContainerStyle,
76
+ ]}
59
77
  showsVerticalScrollIndicator={!hideScrollIndicator}
60
78
  keyboardShouldPersistTaps={keyboardAvoiding ? 'handled' : 'never'}
61
79
  refreshControl={refreshControl}
@@ -67,14 +85,17 @@ export const ScreenLayout: React.FC<ScreenLayoutProps> = ({
67
85
  {children}
68
86
  </View>
69
87
  )}
70
- {footer}
88
+ {footer && (
89
+ <View style={{ paddingBottom }}>
90
+ {footer}
91
+ </View>
92
+ )}
71
93
  </View>
72
94
  );
73
95
 
74
96
  return (
75
- <SafeAreaView
97
+ <View
76
98
  style={[styles.container, { backgroundColor: bgColor }, containerStyle]}
77
- edges={edges}
78
99
  testID={testID}
79
100
  >
80
101
  {keyboardAvoiding ? (
@@ -86,7 +107,7 @@ export const ScreenLayout: React.FC<ScreenLayoutProps> = ({
86
107
  {content}
87
108
  </View>
88
109
  )}
89
- </SafeAreaView>
110
+ </View>
90
111
  );
91
112
  };
92
113
 
@@ -25,7 +25,7 @@ export const getScreenLayoutStyles = (
25
25
  responsiveWrapper: {
26
26
  flex: 1,
27
27
  width: '100%',
28
- ...(maxWidth ? { maxWidth, alignSelf: 'flex-start' as const } : {}),
28
+ ...(maxWidth ? { maxWidth, alignSelf: 'center' as const } : {}),
29
29
  },
30
30
  content: {
31
31
  flex: 1,
@@ -48,7 +48,7 @@ export const StepHeader: React.FC<StepHeaderProps> = ({
48
48
  style,
49
49
  }) => {
50
50
  const tokens = useAppDesignTokens();
51
- const cfg = { ...DEFAULT_CONFIG, ...config };
51
+ const cfg = useMemo(() => ({ ...DEFAULT_CONFIG, ...config }), [config]);
52
52
 
53
53
  const styles = useMemo(
54
54
  () =>
@@ -15,7 +15,7 @@ export function useAlertDismissHandler(alert: Alert) {
15
15
  const handleDismiss = useCallback(() => {
16
16
  dismissAlert(alert.id);
17
17
  alert.onDismiss?.();
18
- }, [alert.id, alert.onDismiss, dismissAlert]);
18
+ }, [alert, dismissAlert]);
19
19
 
20
20
  return handleDismiss;
21
21
  }
@@ -64,7 +64,7 @@ export const FilterBottomSheet = forwardRef<BottomSheetModalRef, FilterBottomShe
64
64
  scrollView: { flex: 1 },
65
65
  }), [tokens]);
66
66
 
67
- const safeSelectedIds = selectedIds ?? [];
67
+ const safeSelectedIds = useMemo(() => selectedIds ?? [], [selectedIds]);
68
68
 
69
69
  const renderOption = useCallback((option: FilterOption, categoryId: string) => {
70
70
  const isSelected = safeSelectedIds.includes(option.id);
@@ -85,7 +85,7 @@ export function TabsNavigator<T extends ParamListBase>({
85
85
  ...customOptions,
86
86
  };
87
87
  },
88
- [defaultScreenOptions, config.screenOptions]
88
+ [defaultScreenOptions, config]
89
89
  );
90
90
 
91
91
  if (visibleScreens.length === 0) {
@@ -30,7 +30,7 @@ const getIconNameForState = (baseName: string, focused: boolean): string => {
30
30
  export function useTabConfig<T extends ParamListBase>(props: UseTabConfigProps<T>): TabNavigatorConfig<T> {
31
31
  const { config } = props;
32
32
  const tokens = useAppDesignTokens();
33
- const { tabBarConfig, insets } = useResponsive();
33
+ const { tabBarConfig } = useResponsive();
34
34
 
35
35
  const finalConfig: TabNavigatorConfig<T> = useMemo(() => {
36
36
  const screens = config.screens as TabScreen<T>[];
@@ -109,7 +109,7 @@ export function useTabConfig<T extends ParamListBase>(props: UseTabConfigProps<T
109
109
  : {}),
110
110
  },
111
111
  };
112
- }, [tokens, config, tabBarConfig, insets]);
112
+ }, [tokens, config, tabBarConfig]);
113
113
 
114
114
  return finalConfig;
115
115
  }
@@ -38,7 +38,7 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({
38
38
  const handleTimeout = useCallback(() => {
39
39
  setTimedOut(true);
40
40
  onTimeout?.();
41
- }, [maxDuration, onTimeout]);
41
+ }, [onTimeout]);
42
42
 
43
43
  useEffect(() => {
44
44
  onReady?.();
@@ -16,7 +16,6 @@ try {
16
16
  const { useVideoPlayer, VideoView } = require('expo-video'); // eslint-disable-line @typescript-eslint/no-require-imports
17
17
 
18
18
  BackgroundVideoImpl = ({ source, overlayOpacity = 0.5 }: BackgroundVideoProps) => {
19
- // eslint-disable-next-line react-hooks/rules-of-hooks
20
19
  const player = useVideoPlayer(source, (p: any) => {
21
20
  p.loop = true;
22
21
  p.play();
@@ -11,7 +11,6 @@ export const getResponsiveVerticalPadding = (
11
11
  ): number => {
12
12
  try {
13
13
  validateSafeAreaInsets(insets);
14
- const { top = 0 } = insets;
15
14
  const isTabletDevice = isTablet();
16
15
  const isSmall = isSmallPhone();
17
16
  const spacingMultiplier = getSpacingMultiplier();
@@ -27,8 +26,8 @@ export const getResponsiveVerticalPadding = (
27
26
  // Apply spacing multiplier for consistency
28
27
  const adjustedPadding = basePadding * spacingMultiplier;
29
28
 
30
- // Ensure minimum padding respects safe area
31
- return Math.max(adjustedPadding, top > 0 ? 8 : adjustedPadding);
29
+ // We now return the base padding; safe areas are handled by ScreenLayout
30
+ return adjustedPadding;
32
31
  } catch {
33
32
  return LAYOUT_CONSTANTS.VERTICAL_PADDING_STANDARD;
34
33
  }
@@ -1,25 +1,22 @@
1
1
  /**
2
2
  * Safe Area Module
3
- * Configurable safe area provider and hooks for React Native apps
3
+ * Refactored: Simplified to only provide essential safe area utilities.
4
+ *
5
+ * We use manual inset management in ScreenLayout for maximum stability
6
+ * across different React Native versions and devices.
4
7
  */
5
8
 
6
- export { SafeAreaProvider, useSafeAreaConfig } from './components/SafeAreaProvider';
7
- export type { SafeAreaProviderProps, SafeAreaConfig } from './components/SafeAreaProvider';
8
-
9
9
  export { useSafeAreaInsets } from './hooks/useSafeAreaInsets';
10
- export { useStatusBarSafeAreaPadding } from './hooks/useStatusBarSafeAreaPadding';
11
- export type { StatusBarPaddingOptions } from './hooks/useStatusBarSafeAreaPadding';
12
-
13
- export { useHeaderSafeAreaPadding } from './hooks/useHeaderSafeAreaPadding';
14
- export type { HeaderPaddingOptions } from './hooks/useHeaderSafeAreaPadding';
15
-
16
- export { useContentSafeAreaPadding } from './hooks/useContentSafeAreaPadding';
17
- export type { ContentPaddingOptions, ContentPaddingResult } from './hooks/useContentSafeAreaPadding';
18
10
 
19
- export { SAFE_AREA_DEFAULTS, PLATFORM_BEHAVIORS, DEFAULT_CONFIG } from './constants';
20
- export { useStableOptions, clearPerformanceCaches } from './utils/optimization';
21
- export { validateNumericInput, throttledWarn, clearValidationCache } from './utils/validation';
11
+ // Re-export essential components from react-native-safe-area-context
12
+ // This provides a single entry point for all safe area needs
13
+ export {
14
+ SafeAreaProvider,
15
+ initialWindowMetrics,
16
+ SafeAreaView
17
+ } from 'react-native-safe-area-context';
22
18
 
23
- // Re-export from react-native-safe-area-context for convenience
24
- export { initialWindowMetrics, SafeAreaView } from 'react-native-safe-area-context';
25
- export type { Edge } from 'react-native-safe-area-context';
19
+ export type {
20
+ Edge,
21
+ SafeAreaProviderProps
22
+ } from 'react-native-safe-area-context';
@@ -8,16 +8,9 @@ import { createStore } from '../../domain/factories/StoreFactory';
8
8
  import type { StoreConfig } from '../../domain/types/Store';
9
9
 
10
10
  export function useStore<T extends object>(config: StoreConfig<T>) {
11
- // Stabilize entire config to track all property changes
12
11
  const stableConfig = useMemo(
13
12
  () => config,
14
- [
15
- config.name,
16
- config.version,
17
- config.persist,
18
- config.storage,
19
- JSON.stringify(config.initialState), // Track initialState changes
20
- ]
13
+ [config]
21
14
  );
22
15
 
23
16
  const store = useMemo(() => createStore(stableConfig), [stableConfig]);
@@ -32,8 +32,9 @@ export function usePrefetchQuery<
32
32
 
33
33
  // Cleanup on unmount
34
34
  useEffect(() => {
35
+ const currentRef = prefetchingRef.current;
35
36
  return () => {
36
- prefetchingRef.current.clear();
37
+ currentRef.clear();
37
38
  };
38
39
  }, []);
39
40
 
@@ -83,7 +83,17 @@ export const DesignSystemProvider: React.FC<DesignSystemProviderProps> = ({
83
83
  setIsInitialized(true);
84
84
  onError?.(error);
85
85
  });
86
- }, []);
86
+ }, [
87
+ customColors,
88
+ initialThemeMode,
89
+ initialize,
90
+ onError,
91
+ setCustomColors,
92
+ setDefaultColors,
93
+ setDefaultThemeMode,
94
+ setGlobalCustomColors,
95
+ setGlobalThemeMode,
96
+ ]);
87
97
 
88
98
  useEffect(() => {
89
99
  if (isInitialized && fontsLoaded) {
@@ -76,13 +76,5 @@ export type ColorVariant =
76
76
  | 'success'
77
77
  | 'error'
78
78
  | 'warning'
79
- | 'info'
80
- // Legacy support (deprecated - use textPrimary/textSecondary instead)
81
- | 'primary'
82
- | 'secondary'
83
- | 'tertiary'
84
- | 'disabled'
85
- | 'inverse'
86
- // Legacy: surfaceVariant is a background color, maps to textSecondary
87
- | 'surfaceVariant';
79
+ | 'info';
88
80
 
@@ -10,7 +10,7 @@ import type { DesignTokens } from '../../../theme';
10
10
  const COLOR_VARIANT_SET = new Set<string>([
11
11
  'textPrimary', 'textSecondary', 'textTertiary', 'textDisabled', 'textInverse',
12
12
  'onSurface', 'onBackground', 'onPrimary', 'onSecondary', 'onSuccess', 'onError', 'onWarning', 'onInfo',
13
- 'success', 'error', 'warning', 'info', 'primary', 'secondary', 'tertiary', 'disabled', 'inverse', 'surfaceVariant',
13
+ 'success', 'error', 'warning', 'info',
14
14
  ]);
15
15
 
16
16
  const COLOR_MAP: Record<ColorVariant, keyof DesignTokens['colors']> = {
@@ -31,12 +31,6 @@ const COLOR_MAP: Record<ColorVariant, keyof DesignTokens['colors']> = {
31
31
  error: 'error',
32
32
  warning: 'warning',
33
33
  info: 'info',
34
- primary: 'primary',
35
- secondary: 'secondary',
36
- tertiary: 'textTertiary',
37
- disabled: 'textDisabled',
38
- inverse: 'textInverse',
39
- surfaceVariant: 'surfaceVariant',
40
34
  };
41
35
 
42
36
  const colorCache = new Map<string, string>();
@@ -182,7 +182,7 @@ export function useAsyncOperation<T, E = Error>(
182
182
  if (immediate && !skip) {
183
183
  execute();
184
184
  }
185
- }, [immediate, skip]);
185
+ }, [immediate, skip, execute]);
186
186
 
187
187
  // Derived state
188
188
  const isSuccess = !isIdle && !isLoading && error === null && data !== null;
@@ -1,15 +0,0 @@
1
- /**
2
- * Tests for SafeAreaProvider component
3
- */
4
- import { describe, it, expect } from '@jest/globals';
5
- import { SafeAreaProvider, useSafeAreaConfig } from '../../components/SafeAreaProvider';
6
-
7
- describe('SafeAreaProvider', () => {
8
- it('should be defined', () => {
9
- expect(SafeAreaProvider).toBeDefined();
10
- });
11
-
12
- it('should have useSafeAreaConfig export', () => {
13
- expect(useSafeAreaConfig).toBeDefined();
14
- });
15
- });
@@ -1,15 +0,0 @@
1
- /**
2
- * Tests for useContentSafeAreaPadding hook
3
- */
4
- import { describe, it, expect } from '@jest/globals';
5
- import { useContentSafeAreaPadding } from '../../hooks/useContentSafeAreaPadding';
6
-
7
- describe('useContentSafeAreaPadding', () => {
8
- it('should be defined', () => {
9
- expect(useContentSafeAreaPadding).toBeDefined();
10
- });
11
-
12
- it('should return function', () => {
13
- expect(typeof useContentSafeAreaPadding).toBe('function');
14
- });
15
- });
@@ -1,15 +0,0 @@
1
- /**
2
- * Tests for useHeaderSafeAreaPadding hook
3
- */
4
- import { describe, it, expect } from '@jest/globals';
5
- import { useHeaderSafeAreaPadding } from '../../hooks/useHeaderSafeAreaPadding';
6
-
7
- describe('useHeaderSafeAreaPadding', () => {
8
- it('should be defined', () => {
9
- expect(useHeaderSafeAreaPadding).toBeDefined();
10
- });
11
-
12
- it('should return function', () => {
13
- expect(typeof useHeaderSafeAreaPadding).toBe('function');
14
- });
15
- });
@@ -1,15 +0,0 @@
1
- /**
2
- * Tests for useSafeAreaInsets hook
3
- */
4
- import { describe, it, expect } from '@jest/globals';
5
- import { useSafeAreaInsets } from '../../hooks/useSafeAreaInsets';
6
-
7
- describe('useSafeAreaInsets', () => {
8
- it('should be defined', () => {
9
- expect(useSafeAreaInsets).toBeDefined();
10
- });
11
-
12
- it('should return function', () => {
13
- expect(typeof useSafeAreaInsets).toBe('function');
14
- });
15
- });
@@ -1,15 +0,0 @@
1
- /**
2
- * Tests for useStatusBarSafeAreaPadding hook
3
- */
4
- import { describe, it, expect } from '@jest/globals';
5
- import { useStatusBarSafeAreaPadding } from '../../hooks/useStatusBarSafeAreaPadding';
6
-
7
- describe('useStatusBarSafeAreaPadding', () => {
8
- it('should be defined', () => {
9
- expect(useStatusBarSafeAreaPadding).toBeDefined();
10
- });
11
-
12
- it('should return function', () => {
13
- expect(typeof useStatusBarSafeAreaPadding).toBe('function');
14
- });
15
- });
@@ -1,26 +0,0 @@
1
- /**
2
- * Integration tests for complete flow
3
- */
4
- import { describe, it, expect } from '@jest/globals';
5
- import { useSafeAreaInsets } from '../../hooks/useSafeAreaInsets';
6
- import { useStatusBarSafeAreaPadding } from '../../hooks/useStatusBarSafeAreaPadding';
7
- import { useHeaderSafeAreaPadding } from '../../hooks/useHeaderSafeAreaPadding';
8
- import { useContentSafeAreaPadding } from '../../hooks/useContentSafeAreaPadding';
9
-
10
- describe('Integration Tests', () => {
11
- it('should import useSafeAreaInsets', () => {
12
- expect(useSafeAreaInsets).toBeDefined();
13
- });
14
-
15
- it('should import useStatusBarSafeAreaPadding', () => {
16
- expect(useStatusBarSafeAreaPadding).toBeDefined();
17
- });
18
-
19
- it('should import useHeaderSafeAreaPadding', () => {
20
- expect(useHeaderSafeAreaPadding).toBeDefined();
21
- });
22
-
23
- it('should import useContentSafeAreaPadding', () => {
24
- expect(useContentSafeAreaPadding).toBeDefined();
25
- });
26
- });
@@ -1,50 +0,0 @@
1
- /**
2
- * Jest setup file
3
- */
4
-
5
- // Define __DEV__ for tests
6
- (global as any).__DEV__ = true;
7
-
8
- // Mock react-native-safe-area-context
9
- jest.mock('react-native-safe-area-context', () => ({
10
- SafeAreaProvider: ({ children }: { children: React.ReactNode }) => children,
11
- useSafeAreaInsets: () => ({
12
- top: 44,
13
- bottom: 34,
14
- left: 0,
15
- right: 0,
16
- }),
17
- }));
18
-
19
- // Mock SafeAreaProvider to avoid JSX issues
20
- jest.mock('../components/SafeAreaProvider', () => ({
21
- SafeAreaProvider: ({ children }: { children: React.ReactNode }) => children,
22
- useSafeAreaConfig: () => ({
23
- minHeaderPadding: 0,
24
- minContentPadding: 0,
25
- minStatusBarPadding: 0,
26
- additionalPadding: 0,
27
- iosStatusBarUsesSafeArea: true,
28
- }),
29
- }), { virtual: true });
30
-
31
- // Mock Platform
32
- jest.mock('react-native', () => ({
33
- Platform: {
34
- OS: 'ios',
35
- select: (obj: Record<string, any>) => obj.ios,
36
- },
37
- }));
38
-
39
- // Mock performance API for performance tests
40
- global.performance = {
41
- ...global.performance,
42
- now: jest.fn(() => Date.now()),
43
- };
44
-
45
- // Simple test to make test suite valid
46
- describe('setup', () => {
47
- it('should define __DEV__', () => {
48
- expect((global as any).__DEV__).toBe(true);
49
- });
50
- });
@@ -1,18 +0,0 @@
1
- /**
2
- * Performance tests for utilities
3
- */
4
-
5
- describe('Performance Tests', () => {
6
- it('should complete quickly', () => {
7
- const startTime = performance.now();
8
-
9
- // Simple operation
10
- const result = Math.random();
11
-
12
- const endTime = performance.now();
13
- const duration = endTime - startTime;
14
-
15
- expect(duration).toBeLessThan(100);
16
- expect(typeof result).toBe('number');
17
- });
18
- });
@@ -1,44 +0,0 @@
1
- /**
2
- * Simple test utilities for safe area package
3
- */
4
- import { describe, it, expect, jest } from '@jest/globals';
5
-
6
- export const mockSafeAreaInsets = {
7
- top: 44,
8
- bottom: 34,
9
- left: 0,
10
- right: 0,
11
- };
12
-
13
- export const createWrapper = () => {
14
- return ({ children }: { children: any }) => children;
15
- };
16
-
17
- export const renderHookWithSafeArea = <T, P>(
18
- hook: (props: P) => T,
19
- options?: {
20
- initialProps?: P;
21
- wrapper?: any;
22
- insets?: typeof mockSafeAreaInsets;
23
- },
24
- ) => {
25
- // wrapper not used in this stub implementation
26
-
27
- return {
28
- result: { current: hook(options?.initialProps as P) },
29
- rerender: () => { },
30
- };
31
- };
32
-
33
- export const resetMocks = () => {
34
- jest.clearAllMocks();
35
- jest.resetModules();
36
- };
37
-
38
- describe('testUtils', () => {
39
- it('should export functions', () => {
40
- expect(typeof mockSafeAreaInsets).toBe('object');
41
- expect(typeof createWrapper).toBe('function');
42
- expect(typeof resetMocks).toBe('function');
43
- });
44
- });
@@ -1,46 +0,0 @@
1
- /**
2
- * SafeAreaProvider Component
3
- * Enhanced wrapper around react-native-safe-area-context with configurable defaults
4
- */
5
-
6
- import React, { createContext, useContext } from 'react';
7
- import {
8
- SafeAreaProvider as NativeSafeAreaProvider,
9
- } from 'react-native-safe-area-context';
10
- import type { SafeAreaProviderProps as NativeSafeAreaProviderProps } from 'react-native-safe-area-context';
11
- import { DEFAULT_CONFIG } from '../constants';
12
-
13
- export interface SafeAreaConfig {
14
- minHeaderPadding: number;
15
- minContentPadding: number;
16
- minStatusBarPadding: number;
17
- additionalPadding: number;
18
- iosStatusBarUsesSafeArea: boolean;
19
- }
20
-
21
- export interface SafeAreaProviderProps extends NativeSafeAreaProviderProps {
22
- children?: React.ReactNode;
23
- config?: SafeAreaConfig;
24
- }
25
-
26
- const SafeAreaConfigContext = createContext<SafeAreaConfig>(DEFAULT_CONFIG);
27
-
28
- export const useSafeAreaConfig = (): SafeAreaConfig => {
29
- return useContext(SafeAreaConfigContext);
30
- };
31
-
32
- export const SafeAreaProvider: React.FC<SafeAreaProviderProps> = ({
33
- children,
34
- config,
35
- ...nativeProps
36
- }) => {
37
- const mergedConfig = { ...DEFAULT_CONFIG, ...config };
38
-
39
- return (
40
- <SafeAreaConfigContext.Provider value={mergedConfig}>
41
- <NativeSafeAreaProvider {...nativeProps}>
42
- {children}
43
- </NativeSafeAreaProvider>
44
- </SafeAreaConfigContext.Provider>
45
- );
46
- };
@@ -1,41 +0,0 @@
1
- /**
2
- * useContentSafeAreaPadding Hook
3
- * Calculate safe area padding for content components
4
- */
5
-
6
- import { useMemo } from 'react';
7
- import { useSafeAreaInsets } from 'react-native-safe-area-context';
8
- import { useSafeAreaConfig } from '../components/SafeAreaProvider';
9
- import { useStableOptions } from '../utils/optimization';
10
- import { validateNumericInput } from '../utils/validation';
11
-
12
- export interface ContentPaddingOptions {
13
- minBottomPadding?: number;
14
- additionalPadding?: number;
15
- }
16
-
17
- export interface ContentPaddingResult {
18
- paddingBottom: number;
19
- }
20
-
21
- export const useContentSafeAreaPadding = (
22
- options: ContentPaddingOptions = {},
23
- ): ContentPaddingResult => {
24
- const insets = useSafeAreaInsets();
25
- const config = useSafeAreaConfig();
26
- const stableOptions = useStableOptions(options);
27
- const minBottomPadding = stableOptions.minBottomPadding ?? config.minContentPadding;
28
- const additionalPadding = stableOptions.additionalPadding ?? config.additionalPadding;
29
-
30
- // Validate inputs once
31
- useMemo(() => {
32
- validateNumericInput(minBottomPadding, 'useContentSafeAreaPadding.minBottomPadding');
33
- validateNumericInput(additionalPadding, 'useContentSafeAreaPadding.additionalPadding');
34
- }, [minBottomPadding, additionalPadding]);
35
-
36
- const paddingBottom = useMemo(() => {
37
- return Math.max(insets.bottom, minBottomPadding) + additionalPadding;
38
- }, [insets.bottom, minBottomPadding, additionalPadding]);
39
-
40
- return useMemo(() => ({ paddingBottom }), [paddingBottom]);
41
- };
@@ -1,35 +0,0 @@
1
- /**
2
- * useHeaderSafeAreaPadding Hook
3
- * Calculate safe area padding for header components
4
- */
5
-
6
- import { useMemo } from 'react';
7
- import { useSafeAreaInsets } from 'react-native-safe-area-context';
8
- import { useSafeAreaConfig } from '../components/SafeAreaProvider';
9
- import { useStableOptions } from '../utils/optimization';
10
- import { validateNumericInput } from '../utils/validation';
11
-
12
- export interface HeaderPaddingOptions {
13
- minPadding?: number;
14
- additionalPadding?: number;
15
- }
16
-
17
- export const useHeaderSafeAreaPadding = (
18
- options: HeaderPaddingOptions = {},
19
- ): number => {
20
- const insets = useSafeAreaInsets();
21
- const config = useSafeAreaConfig();
22
- const stableOptions = useStableOptions(options);
23
- const minPadding = stableOptions.minPadding ?? config.minHeaderPadding;
24
- const additionalPadding = stableOptions.additionalPadding ?? config.additionalPadding;
25
-
26
- // Validate inputs once
27
- useMemo(() => {
28
- validateNumericInput(minPadding, 'useHeaderSafeAreaPadding.minPadding');
29
- validateNumericInput(additionalPadding, 'useHeaderSafeAreaPadding.additionalPadding');
30
- }, [minPadding, additionalPadding]);
31
-
32
- return useMemo(() => {
33
- return Math.max(insets.top, minPadding) + additionalPadding;
34
- }, [insets.top, minPadding, additionalPadding]);
35
- };
@@ -1,36 +0,0 @@
1
- /**
2
- * useStatusBarSafeAreaPadding Hook
3
- * Calculate safe area padding for status bar components
4
- */
5
-
6
- import { useMemo } from 'react';
7
- import { Platform } from 'react-native';
8
- import { useSafeAreaInsets } from 'react-native-safe-area-context';
9
- import { useSafeAreaConfig } from '../components/SafeAreaProvider';
10
- import { useStableOptions } from '../utils/optimization';
11
- import { validateNumericInput } from '../utils/validation';
12
-
13
- export interface StatusBarPaddingOptions {
14
- minPadding?: number;
15
- }
16
-
17
- export const useStatusBarSafeAreaPadding = (
18
- options: StatusBarPaddingOptions = {},
19
- ): number => {
20
- const insets = useSafeAreaInsets();
21
- const config = useSafeAreaConfig();
22
- const stableOptions = useStableOptions(options);
23
- const minPadding = stableOptions.minPadding ?? config.minStatusBarPadding;
24
-
25
- // Validate input once
26
- useMemo(() => {
27
- validateNumericInput(minPadding, 'useStatusBarSafeAreaPadding.minPadding');
28
- }, [minPadding]);
29
-
30
- return useMemo(() => {
31
- if (Platform.OS === 'ios' && config.iosStatusBarUsesSafeArea) {
32
- return minPadding;
33
- }
34
- return Math.max(insets.top, minPadding);
35
- }, [insets.top, minPadding, config.iosStatusBarUsesSafeArea]);
36
- };
@@ -1,49 +0,0 @@
1
- /**
2
- * Performance optimization utilities for safe area hooks
3
- */
4
-
5
- import { useMemo, useRef } from "react";
6
- import { clearValidationCache } from "./validation";
7
-
8
- /**
9
- * Memoize options object to prevent unnecessary re-renders
10
- * Uses shallow comparison for better performance
11
- */
12
- export const useStableOptions = <T extends Record<string, any>>(
13
- options: T,
14
- ): T => {
15
- const prevOptionsRef = useRef<T | undefined>(undefined);
16
-
17
- return useMemo(() => {
18
- const prev = prevOptionsRef.current;
19
-
20
- if (!prev) {
21
- prevOptionsRef.current = options;
22
- return options;
23
- }
24
-
25
- const prevKeys = Object.keys(prev);
26
- const currentKeys = Object.keys(options);
27
-
28
- if (prevKeys.length !== currentKeys.length) {
29
- prevOptionsRef.current = options;
30
- return options;
31
- }
32
-
33
- const hasChanged = prevKeys.some((key) => prev[key] !== options[key]);
34
-
35
- if (hasChanged) {
36
- prevOptionsRef.current = options;
37
- }
38
-
39
- return prevOptionsRef.current ?? options;
40
- }, [options]);
41
- };
42
-
43
- /**
44
- * Cleanup function to clear all performance caches
45
- * Call this in useEffect cleanup if needed
46
- */
47
- export const clearPerformanceCaches = (): void => {
48
- clearValidationCache();
49
- };
@@ -1,50 +0,0 @@
1
- /**
2
- * Validation utilities for safe area hooks
3
- */
4
-
5
- // Validate numeric input with performance optimization
6
- // Caches validation results to avoid repeated checks
7
- const validationCache = new Map<string, boolean>();
8
-
9
- export const validateNumericInput = (
10
- value: number,
11
- name: string,
12
- allowNegative = false,
13
- ): boolean => {
14
- if (!__DEV__) {
15
- return true;
16
- }
17
-
18
- const cacheKey = `${name}:${value}:${allowNegative}`;
19
-
20
- if (validationCache.has(cacheKey)) {
21
- return validationCache.get(cacheKey)!;
22
- }
23
-
24
- const isValid = typeof value === 'number' && !isNaN(value) && (allowNegative || value >= 0);
25
-
26
- if (!isValid) {
27
- throttledWarn(`${name}: must be a ${allowNegative ? 'number' : 'non-negative number'}, got ${value}`);
28
- }
29
-
30
- // Limit cache size to prevent memory leaks
31
- if (validationCache.size > 100) {
32
- const firstKey = validationCache.keys().next().value;
33
- if (firstKey) {
34
- validationCache.delete(firstKey);
35
- }
36
- }
37
-
38
- validationCache.set(cacheKey, isValid);
39
- return isValid;
40
- };
41
-
42
- export const throttledWarn = (_message: string): void => {
43
- // Silent validation - no console output in production
44
- void _message;
45
- };
46
-
47
- // Cleanup function to clear validation cache
48
- export const clearValidationCache = (): void => {
49
- validationCache.clear();
50
- };