@umituz/react-native-design-system 2.3.0 → 2.3.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 (50) hide show
  1. package/package.json +16 -4
  2. package/src/atoms/AtomicBadge.tsx +121 -0
  3. package/src/atoms/AtomicInput.tsx +0 -1
  4. package/src/atoms/AtomicPicker.tsx +0 -1
  5. package/src/atoms/index.ts +8 -0
  6. package/src/atoms/picker/components/PickerChips.tsx +0 -1
  7. package/src/atoms/picker/components/PickerModal.tsx +1 -3
  8. package/src/atoms/picker/styles/pickerStyles.ts +1 -1
  9. package/src/device/domain/entities/Device.ts +207 -0
  10. package/src/device/domain/entities/DeviceMemoryUtils.ts +62 -0
  11. package/src/device/domain/entities/DeviceTypeUtils.ts +66 -0
  12. package/src/device/domain/entities/__tests__/DeviceMemoryUtils.test.ts +118 -0
  13. package/src/device/domain/entities/__tests__/DeviceTypeUtils.test.ts +104 -0
  14. package/src/device/domain/entities/__tests__/DeviceUtils.test.ts +167 -0
  15. package/src/device/index.ts +51 -0
  16. package/src/device/infrastructure/services/ApplicationInfoService.ts +86 -0
  17. package/src/device/infrastructure/services/DeviceCapabilityService.ts +60 -0
  18. package/src/device/infrastructure/services/DeviceIdService.ts +70 -0
  19. package/src/device/infrastructure/services/DeviceInfoService.ts +95 -0
  20. package/src/device/infrastructure/services/DeviceService.ts +104 -0
  21. package/src/device/infrastructure/services/PersistentDeviceIdService.ts +132 -0
  22. package/src/device/infrastructure/services/UserFriendlyIdService.ts +68 -0
  23. package/src/device/infrastructure/utils/__tests__/nativeModuleUtils.test.ts +158 -0
  24. package/src/device/infrastructure/utils/__tests__/stringUtils.test.ts +120 -0
  25. package/src/device/infrastructure/utils/nativeModuleUtils.ts +69 -0
  26. package/src/device/infrastructure/utils/stringUtils.ts +59 -0
  27. package/src/device/presentation/hooks/useAnonymousUser.ts +117 -0
  28. package/src/device/presentation/hooks/useDeviceInfo.ts +222 -0
  29. package/src/index.ts +4 -0
  30. package/src/molecules/ConfirmationModalContent.tsx +4 -4
  31. package/src/molecules/ConfirmationModalMain.tsx +1 -1
  32. package/src/molecules/ScreenHeader.tsx +2 -2
  33. package/src/molecules/confirmation-modal/components.tsx +1 -1
  34. package/src/molecules/confirmation-modal/styles/confirmationModalStyles.ts +6 -7
  35. package/src/presentation/utils/variants/__tests__/core.test.ts +0 -1
  36. package/src/responsive/deviceDetection.ts +5 -5
  37. package/src/responsive/iPadBreakpoints.ts +55 -0
  38. package/src/responsive/iPadDetection.ts +48 -0
  39. package/src/responsive/iPadLayoutUtils.ts +95 -0
  40. package/src/responsive/iPadModalUtils.ts +98 -0
  41. package/src/responsive/index.ts +31 -0
  42. package/src/safe-area/__tests__/components/SafeAreaProvider.test.tsx +2 -2
  43. package/src/safe-area/__tests__/hooks/useContentSafeAreaPadding.test.tsx +2 -2
  44. package/src/safe-area/__tests__/hooks/useHeaderSafeAreaPadding.test.tsx +2 -2
  45. package/src/safe-area/__tests__/hooks/useSafeAreaInsets.test.tsx +2 -2
  46. package/src/safe-area/__tests__/hooks/useStatusBarSafeAreaPadding.test.tsx +2 -2
  47. package/src/safe-area/__tests__/integration/completeFlow.test.tsx +5 -4
  48. package/src/safe-area/__tests__/utils/testUtils.tsx +5 -4
  49. package/src/theme/infrastructure/stores/themeStore.ts +0 -2
  50. package/src/typography/presentation/utils/textColorUtils.ts +0 -1
@@ -0,0 +1,48 @@
1
+ /**
2
+ * iPad Device Detection Utilities
3
+ */
4
+
5
+ import { Dimensions, Platform } from 'react-native';
6
+ import { DEVICE_BREAKPOINTS } from './config';
7
+ import { IPAD_BREAKPOINTS } from './iPadBreakpoints';
8
+
9
+ /**
10
+ * Detect if the current device is an iPad
11
+ */
12
+ export function isIPad(): boolean {
13
+ if (Platform.OS !== 'ios') return false;
14
+
15
+ const { width, height } = Dimensions.get('window');
16
+ const minDimension = Math.min(width, height);
17
+ return minDimension >= DEVICE_BREAKPOINTS.SMALL_TABLET;
18
+ }
19
+
20
+ /**
21
+ * Detect if the current device is an iPad mini
22
+ */
23
+ export function isIPadMini(): boolean {
24
+ if (!isIPad()) return false;
25
+
26
+ const { width, height } = Dimensions.get('window');
27
+ const minWidth = Math.min(width, height);
28
+ return minWidth < IPAD_BREAKPOINTS.IPAD_AIR;
29
+ }
30
+
31
+ /**
32
+ * Detect if the current device is an iPad Pro (12.9")
33
+ */
34
+ export function isIPadPro(): boolean {
35
+ if (!isIPad()) return false;
36
+
37
+ const { width, height } = Dimensions.get('window');
38
+ const minWidth = Math.min(width, height);
39
+ return minWidth >= IPAD_BREAKPOINTS.IPAD_11_PRO;
40
+ }
41
+
42
+ /**
43
+ * Check if device is in landscape orientation
44
+ */
45
+ export function isIPadLandscape(): boolean {
46
+ const { width, height } = Dimensions.get('window');
47
+ return width > height;
48
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * iPad Layout Utilities
3
+ */
4
+
5
+ import { Dimensions } from 'react-native';
6
+ import { DEVICE_BREAKPOINTS } from './config';
7
+ import {
8
+ IPAD_BREAKPOINTS,
9
+ TOUCH_TARGETS,
10
+ CONTENT_WIDTH_CONSTRAINTS,
11
+ IPAD_LAYOUT_CONFIG,
12
+ } from './iPadBreakpoints';
13
+ import { isIPad, isIPadPro } from './iPadDetection';
14
+
15
+ /**
16
+ * Get optimal content max width based on screen size
17
+ */
18
+ export function getContentMaxWidth(screenWidth: number): number {
19
+ if (screenWidth >= IPAD_BREAKPOINTS.IPAD_12_9_PRO) {
20
+ return CONTENT_WIDTH_CONSTRAINTS.READABLE_MAX;
21
+ }
22
+ if (screenWidth >= IPAD_BREAKPOINTS.IPAD_AIR) {
23
+ return Math.min(screenWidth * 0.85, CONTENT_WIDTH_CONSTRAINTS.READABLE_MAX);
24
+ }
25
+ if (screenWidth >= DEVICE_BREAKPOINTS.SMALL_TABLET) {
26
+ return Math.min(screenWidth * 0.90, CONTENT_WIDTH_CONSTRAINTS.FORM_MAX);
27
+ }
28
+ return screenWidth;
29
+ }
30
+
31
+ /**
32
+ * Get grid columns based on screen width
33
+ */
34
+ export function getIPadGridColumns(screenWidth: number): number {
35
+ if (screenWidth >= IPAD_BREAKPOINTS.IPAD_LANDSCAPE_AIR) return 4;
36
+ if (screenWidth >= IPAD_BREAKPOINTS.IPAD_12_9_PRO) return 3;
37
+ if (screenWidth >= DEVICE_BREAKPOINTS.SMALL_TABLET) return 2;
38
+ return 1;
39
+ }
40
+
41
+ /**
42
+ * Get touch target size based on device
43
+ */
44
+ export function getTouchTargetSize(): number {
45
+ return isIPad() ? TOUCH_TARGETS.IPAD_RECOMMENDED : TOUCH_TARGETS.RECOMMENDED;
46
+ }
47
+
48
+ /**
49
+ * Get screen padding based on device type
50
+ */
51
+ export function getIPadScreenPadding(): number {
52
+ if (isIPadPro()) return 32;
53
+ if (isIPad()) return IPAD_LAYOUT_CONFIG.SCREEN_PADDING;
54
+ return 16;
55
+ }
56
+
57
+ /**
58
+ * Get font scale for iPad
59
+ */
60
+ export function getIPadFontScale(): number {
61
+ if (isIPadPro()) return 1.15;
62
+ if (isIPad()) return IPAD_LAYOUT_CONFIG.FONT_SCALE;
63
+ return 1.0;
64
+ }
65
+
66
+ export interface IPadLayoutInfo {
67
+ isIPad: boolean;
68
+ isLandscape: boolean;
69
+ screenWidth: number;
70
+ screenHeight: number;
71
+ contentMaxWidth: number;
72
+ gridColumns: number;
73
+ touchTargetSize: number;
74
+ screenPadding: number;
75
+ fontScale: number;
76
+ }
77
+
78
+ /**
79
+ * Get complete iPad layout information
80
+ */
81
+ export function getIPadLayoutInfo(): IPadLayoutInfo {
82
+ const { width, height } = Dimensions.get('window');
83
+
84
+ return {
85
+ isIPad: isIPad(),
86
+ isLandscape: width > height,
87
+ screenWidth: width,
88
+ screenHeight: height,
89
+ contentMaxWidth: getContentMaxWidth(width),
90
+ gridColumns: getIPadGridColumns(width),
91
+ touchTargetSize: getTouchTargetSize(),
92
+ screenPadding: getIPadScreenPadding(),
93
+ fontScale: getIPadFontScale(),
94
+ };
95
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * iPad Modal Utilities
3
+ * Modal dimensions optimized for iPad following Apple HIG
4
+ */
5
+
6
+ import { Dimensions } from 'react-native';
7
+ import { CONTENT_WIDTH_CONSTRAINTS, IPAD_LAYOUT_CONFIG } from './iPadBreakpoints';
8
+ import { isIPad, isIPadPro } from './iPadDetection';
9
+
10
+ export interface ModalDimensions {
11
+ width: number | string;
12
+ maxWidth: number;
13
+ maxHeight: string | number;
14
+ horizontalMargin: number;
15
+ borderRadius: number;
16
+ }
17
+
18
+ /**
19
+ * Get modal dimensions optimized for iPad
20
+ */
21
+ export function getIPadModalDimensions(): ModalDimensions {
22
+ const { width } = Dimensions.get('window');
23
+
24
+ if (isIPadPro()) {
25
+ return {
26
+ width: CONTENT_WIDTH_CONSTRAINTS.MODAL_MAX,
27
+ maxWidth: CONTENT_WIDTH_CONSTRAINTS.MODAL_MAX,
28
+ maxHeight: '75%',
29
+ horizontalMargin: 64,
30
+ borderRadius: 20,
31
+ };
32
+ }
33
+
34
+ if (isIPad()) {
35
+ return {
36
+ width: Math.min(width * 0.8, CONTENT_WIDTH_CONSTRAINTS.MODAL_MAX),
37
+ maxWidth: CONTENT_WIDTH_CONSTRAINTS.MODAL_MAX,
38
+ maxHeight: '80%',
39
+ horizontalMargin: IPAD_LAYOUT_CONFIG.MODAL_HORIZONTAL_MARGIN,
40
+ borderRadius: IPAD_LAYOUT_CONFIG.MODAL_CORNER_RADIUS,
41
+ };
42
+ }
43
+
44
+ return {
45
+ width: '92%',
46
+ maxWidth: 480,
47
+ maxHeight: '90%',
48
+ horizontalMargin: 16,
49
+ borderRadius: 24,
50
+ };
51
+ }
52
+
53
+ export interface PaywallDimensions {
54
+ width: number | string;
55
+ maxWidth: number;
56
+ maxHeight: string | number;
57
+ padding: number;
58
+ buttonHeight: number;
59
+ priceTextScale: number;
60
+ }
61
+
62
+ /**
63
+ * Get paywall modal dimensions - Apple HIG compliant
64
+ */
65
+ export function getPaywallDimensions(): PaywallDimensions {
66
+ const { width } = Dimensions.get('window');
67
+
68
+ if (isIPadPro()) {
69
+ return {
70
+ width: CONTENT_WIDTH_CONSTRAINTS.PAYWALL_MAX,
71
+ maxWidth: CONTENT_WIDTH_CONSTRAINTS.PAYWALL_MAX,
72
+ maxHeight: '80%',
73
+ padding: 32,
74
+ buttonHeight: 56,
75
+ priceTextScale: 1.2,
76
+ };
77
+ }
78
+
79
+ if (isIPad()) {
80
+ return {
81
+ width: Math.min(width * 0.75, CONTENT_WIDTH_CONSTRAINTS.PAYWALL_MAX),
82
+ maxWidth: CONTENT_WIDTH_CONSTRAINTS.PAYWALL_MAX,
83
+ maxHeight: '85%',
84
+ padding: 28,
85
+ buttonHeight: 52,
86
+ priceTextScale: 1.1,
87
+ };
88
+ }
89
+
90
+ return {
91
+ width: '100%',
92
+ maxWidth: 480,
93
+ maxHeight: '95%',
94
+ padding: 20,
95
+ buttonHeight: 50,
96
+ priceTextScale: 1.0,
97
+ };
98
+ }
@@ -62,3 +62,34 @@ export {
62
62
  getMinTouchTarget,
63
63
  } from './platformConstants';
64
64
 
65
+ // iPad-specific exports
66
+ export {
67
+ IPAD_BREAKPOINTS,
68
+ TOUCH_TARGETS,
69
+ CONTENT_WIDTH_CONSTRAINTS,
70
+ IPAD_LAYOUT_CONFIG,
71
+ } from './iPadBreakpoints';
72
+
73
+ export {
74
+ isIPad,
75
+ isIPadMini,
76
+ isIPadPro,
77
+ isIPadLandscape,
78
+ } from './iPadDetection';
79
+
80
+ export {
81
+ getContentMaxWidth,
82
+ getIPadGridColumns,
83
+ getTouchTargetSize,
84
+ getIPadScreenPadding,
85
+ getIPadFontScale,
86
+ getIPadLayoutInfo,
87
+ type IPadLayoutInfo,
88
+ } from './iPadLayoutUtils';
89
+
90
+ export {
91
+ getIPadModalDimensions,
92
+ getPaywallDimensions,
93
+ type ModalDimensions,
94
+ type PaywallDimensions,
95
+ } from './iPadModalUtils';
@@ -1,15 +1,15 @@
1
1
  /**
2
2
  * Tests for SafeAreaProvider component
3
3
  */
4
+ import { describe, it, expect } from '@jest/globals';
5
+ import { SafeAreaProvider, useSafeAreaConfig } from '../../components/SafeAreaProvider';
4
6
 
5
7
  describe('SafeAreaProvider', () => {
6
8
  it('should be defined', () => {
7
- const { SafeAreaProvider } = require('../../components/SafeAreaProvider');
8
9
  expect(SafeAreaProvider).toBeDefined();
9
10
  });
10
11
 
11
12
  it('should have useSafeAreaConfig export', () => {
12
- const { useSafeAreaConfig } = require('../../components/SafeAreaProvider');
13
13
  expect(useSafeAreaConfig).toBeDefined();
14
14
  });
15
15
  });
@@ -1,15 +1,15 @@
1
1
  /**
2
2
  * Tests for useContentSafeAreaPadding hook
3
3
  */
4
+ import { describe, it, expect } from '@jest/globals';
5
+ import { useContentSafeAreaPadding } from '../../hooks/useContentSafeAreaPadding';
4
6
 
5
7
  describe('useContentSafeAreaPadding', () => {
6
8
  it('should be defined', () => {
7
- const { useContentSafeAreaPadding } = require('../../hooks/useContentSafeAreaPadding');
8
9
  expect(useContentSafeAreaPadding).toBeDefined();
9
10
  });
10
11
 
11
12
  it('should return function', () => {
12
- const { useContentSafeAreaPadding } = require('../../hooks/useContentSafeAreaPadding');
13
13
  expect(typeof useContentSafeAreaPadding).toBe('function');
14
14
  });
15
15
  });
@@ -1,15 +1,15 @@
1
1
  /**
2
2
  * Tests for useHeaderSafeAreaPadding hook
3
3
  */
4
+ import { describe, it, expect } from '@jest/globals';
5
+ import { useHeaderSafeAreaPadding } from '../../hooks/useHeaderSafeAreaPadding';
4
6
 
5
7
  describe('useHeaderSafeAreaPadding', () => {
6
8
  it('should be defined', () => {
7
- const { useHeaderSafeAreaPadding } = require('../../hooks/useHeaderSafeAreaPadding');
8
9
  expect(useHeaderSafeAreaPadding).toBeDefined();
9
10
  });
10
11
 
11
12
  it('should return function', () => {
12
- const { useHeaderSafeAreaPadding } = require('../../hooks/useHeaderSafeAreaPadding');
13
13
  expect(typeof useHeaderSafeAreaPadding).toBe('function');
14
14
  });
15
15
  });
@@ -1,15 +1,15 @@
1
1
  /**
2
2
  * Tests for useSafeAreaInsets hook
3
3
  */
4
+ import { describe, it, expect } from '@jest/globals';
5
+ import { useSafeAreaInsets } from '../../hooks/useSafeAreaInsets';
4
6
 
5
7
  describe('useSafeAreaInsets', () => {
6
8
  it('should be defined', () => {
7
- const { useSafeAreaInsets } = require('../../hooks/useSafeAreaInsets');
8
9
  expect(useSafeAreaInsets).toBeDefined();
9
10
  });
10
11
 
11
12
  it('should return function', () => {
12
- const { useSafeAreaInsets } = require('../../hooks/useSafeAreaInsets');
13
13
  expect(typeof useSafeAreaInsets).toBe('function');
14
14
  });
15
15
  });
@@ -1,15 +1,15 @@
1
1
  /**
2
2
  * Tests for useStatusBarSafeAreaPadding hook
3
3
  */
4
+ import { describe, it, expect } from '@jest/globals';
5
+ import { useStatusBarSafeAreaPadding } from '../../hooks/useStatusBarSafeAreaPadding';
4
6
 
5
7
  describe('useStatusBarSafeAreaPadding', () => {
6
8
  it('should be defined', () => {
7
- const { useStatusBarSafeAreaPadding } = require('../../hooks/useStatusBarSafeAreaPadding');
8
9
  expect(useStatusBarSafeAreaPadding).toBeDefined();
9
10
  });
10
11
 
11
12
  it('should return function', () => {
12
- const { useStatusBarSafeAreaPadding } = require('../../hooks/useStatusBarSafeAreaPadding');
13
13
  expect(typeof useStatusBarSafeAreaPadding).toBe('function');
14
14
  });
15
15
  });
@@ -1,25 +1,26 @@
1
1
  /**
2
2
  * Integration tests for complete flow
3
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';
4
9
 
5
10
  describe('Integration Tests', () => {
6
11
  it('should import useSafeAreaInsets', () => {
7
- const { useSafeAreaInsets } = require('../../hooks/useSafeAreaInsets');
8
12
  expect(useSafeAreaInsets).toBeDefined();
9
13
  });
10
14
 
11
15
  it('should import useStatusBarSafeAreaPadding', () => {
12
- const { useStatusBarSafeAreaPadding } = require('../../hooks/useStatusBarSafeAreaPadding');
13
16
  expect(useStatusBarSafeAreaPadding).toBeDefined();
14
17
  });
15
18
 
16
19
  it('should import useHeaderSafeAreaPadding', () => {
17
- const { useHeaderSafeAreaPadding } = require('../../hooks/useHeaderSafeAreaPadding');
18
20
  expect(useHeaderSafeAreaPadding).toBeDefined();
19
21
  });
20
22
 
21
23
  it('should import useContentSafeAreaPadding', () => {
22
- const { useContentSafeAreaPadding } = require('../../hooks/useContentSafeAreaPadding');
23
24
  expect(useContentSafeAreaPadding).toBeDefined();
24
25
  });
25
26
  });
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Simple test utilities for safe area package
3
3
  */
4
+ import { describe, it, expect, jest } from '@jest/globals';
4
5
 
5
6
  export const mockSafeAreaInsets = {
6
7
  top: 44,
@@ -9,7 +10,7 @@ export const mockSafeAreaInsets = {
9
10
  right: 0,
10
11
  };
11
12
 
12
- export const createWrapper = (insets = mockSafeAreaInsets) => {
13
+ export const createWrapper = () => {
13
14
  return ({ children }: { children: any }) => children;
14
15
  };
15
16
 
@@ -21,11 +22,11 @@ export const renderHookWithSafeArea = <T, P>(
21
22
  insets?: typeof mockSafeAreaInsets;
22
23
  },
23
24
  ) => {
24
- const wrapper = options?.wrapper || createWrapper(options?.insets);
25
-
25
+ // wrapper not used in this stub implementation
26
+
26
27
  return {
27
28
  result: { current: hook(options?.initialProps as P) },
28
- rerender: () => {},
29
+ rerender: () => { },
29
30
  };
30
31
  };
31
32
 
@@ -66,7 +66,6 @@ export const useTheme = create<ThemeState>()((set: any, get: any) => ({
66
66
  useDesignSystemTheme.getState().setThemeMode('dark');
67
67
  }
68
68
  } catch (error) {
69
- /* eslint-disable-next-line no-console */
70
69
  if (__DEV__) console.error('[ThemeStore] Initialization error:', error);
71
70
  // Silent failure - still mark as initialized to prevent blocking
72
71
  set({ isInitialized: true });
@@ -90,7 +89,6 @@ export const useTheme = create<ThemeState>()((set: any, get: any) => ({
90
89
  // Sync with design system global theme
91
90
  useDesignSystemTheme.getState().setThemeMode(mode);
92
91
  } catch (error) {
93
- /* eslint-disable-next-line no-console */
94
92
  if (__DEV__) console.error('[ThemeStore] Error setting theme mode:', error);
95
93
  }
96
94
  },
@@ -124,7 +124,6 @@ class MaterialColorMapper implements ColorMapper {
124
124
 
125
125
  default:
126
126
  if (__DEV__) {
127
- // eslint-disable-next-line no-console
128
127
  console.warn(`Unknown color variant: ${color}`);
129
128
  }
130
129
  return tokens.colors.textPrimary;