@umituz/react-native-design-system 2.3.1 → 2.3.3
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 +15 -3
- package/src/atoms/AtomicInput.tsx +0 -1
- package/src/atoms/AtomicPicker.tsx +0 -1
- package/src/atoms/picker/components/PickerChips.tsx +0 -1
- package/src/atoms/picker/components/PickerModal.tsx +1 -3
- package/src/atoms/picker/styles/pickerStyles.ts +1 -1
- package/src/{responsive → device/detection}/deviceDetection.ts +7 -7
- package/src/device/detection/iPadBreakpoints.ts +55 -0
- package/src/device/detection/iPadDetection.ts +48 -0
- package/src/device/detection/iPadLayoutUtils.ts +95 -0
- package/src/device/detection/iPadModalUtils.ts +98 -0
- package/src/device/detection/index.ts +54 -0
- package/src/device/domain/entities/Device.ts +207 -0
- package/src/device/domain/entities/DeviceMemoryUtils.ts +62 -0
- package/src/device/domain/entities/DeviceTypeUtils.ts +66 -0
- package/src/device/domain/entities/__tests__/DeviceMemoryUtils.test.ts +118 -0
- package/src/device/domain/entities/__tests__/DeviceTypeUtils.test.ts +104 -0
- package/src/device/domain/entities/__tests__/DeviceUtils.test.ts +167 -0
- package/src/device/index.ts +104 -0
- package/src/device/infrastructure/services/ApplicationInfoService.ts +86 -0
- package/src/device/infrastructure/services/DeviceCapabilityService.ts +60 -0
- package/src/device/infrastructure/services/DeviceIdService.ts +70 -0
- package/src/device/infrastructure/services/DeviceInfoService.ts +95 -0
- package/src/device/infrastructure/services/DeviceService.ts +104 -0
- package/src/device/infrastructure/services/PersistentDeviceIdService.ts +132 -0
- package/src/device/infrastructure/services/UserFriendlyIdService.ts +68 -0
- package/src/device/infrastructure/utils/__tests__/nativeModuleUtils.test.ts +158 -0
- package/src/device/infrastructure/utils/__tests__/stringUtils.test.ts +120 -0
- package/src/device/infrastructure/utils/nativeModuleUtils.ts +69 -0
- package/src/device/infrastructure/utils/stringUtils.ts +59 -0
- package/src/device/presentation/hooks/useAnonymousUser.ts +117 -0
- package/src/device/presentation/hooks/useDeviceInfo.ts +222 -0
- package/src/molecules/ConfirmationModalContent.tsx +4 -4
- package/src/molecules/ConfirmationModalMain.tsx +1 -1
- package/src/molecules/ScreenHeader.tsx +2 -2
- package/src/molecules/confirmation-modal/components.tsx +1 -1
- package/src/molecules/confirmation-modal/styles/confirmationModalStyles.ts +6 -7
- package/src/presentation/utils/variants/__tests__/core.test.ts +0 -1
- package/src/responsive/gridUtils.ts +1 -1
- package/src/responsive/index.ts +36 -20
- package/src/responsive/responsive.ts +2 -2
- package/src/responsive/responsiveLayout.ts +1 -1
- package/src/responsive/responsiveModal.ts +1 -1
- package/src/responsive/responsiveSizing.ts +1 -1
- package/src/responsive/useResponsive.ts +1 -1
- package/src/safe-area/__tests__/components/SafeAreaProvider.test.tsx +2 -2
- package/src/safe-area/__tests__/hooks/useContentSafeAreaPadding.test.tsx +2 -2
- package/src/safe-area/__tests__/hooks/useHeaderSafeAreaPadding.test.tsx +2 -2
- package/src/safe-area/__tests__/hooks/useSafeAreaInsets.test.tsx +2 -2
- package/src/safe-area/__tests__/hooks/useStatusBarSafeAreaPadding.test.tsx +2 -2
- package/src/safe-area/__tests__/integration/completeFlow.test.tsx +5 -4
- package/src/safe-area/__tests__/utils/testUtils.tsx +5 -4
- package/src/theme/infrastructure/stores/themeStore.ts +0 -2
- package/src/typography/presentation/utils/textColorUtils.ts +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-design-system",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.3",
|
|
4
4
|
"description": "Universal design system for React Native apps - Consolidated package with atoms, molecules, organisms, theme, typography, responsive and safe area utilities",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"./typography": "./src/typography/index.ts",
|
|
19
19
|
"./responsive": "./src/responsive/index.ts",
|
|
20
20
|
"./safe-area": "./src/safe-area/index.ts",
|
|
21
|
+
"./device": "./src/device/index.ts",
|
|
21
22
|
"./package.json": "./package.json"
|
|
22
23
|
},
|
|
23
24
|
"scripts": {
|
|
@@ -60,7 +61,9 @@
|
|
|
60
61
|
"react-native-reanimated": ">=3.16.0",
|
|
61
62
|
"react-native-safe-area-context": ">=5.0.0",
|
|
62
63
|
"react-native-svg": ">=15.0.0",
|
|
63
|
-
"zustand": ">=5.0.0"
|
|
64
|
+
"zustand": ">=5.0.0",
|
|
65
|
+
"expo-device": ">=5.0.0",
|
|
66
|
+
"expo-application": ">=5.0.0"
|
|
64
67
|
},
|
|
65
68
|
"peerDependenciesMeta": {
|
|
66
69
|
"expo-linear-gradient": {
|
|
@@ -71,6 +74,12 @@
|
|
|
71
74
|
},
|
|
72
75
|
"@gorhom/bottom-sheet": {
|
|
73
76
|
"optional": true
|
|
77
|
+
},
|
|
78
|
+
"expo-device": {
|
|
79
|
+
"optional": true
|
|
80
|
+
},
|
|
81
|
+
"expo-application": {
|
|
82
|
+
"optional": true
|
|
74
83
|
}
|
|
75
84
|
},
|
|
76
85
|
"devDependencies": {
|
|
@@ -95,7 +104,10 @@
|
|
|
95
104
|
"react-native-safe-area-context": "^5.6.0",
|
|
96
105
|
"react-native-svg": "15.12.1",
|
|
97
106
|
"typescript": "~5.9.2",
|
|
98
|
-
"zustand": "^5.0.2"
|
|
107
|
+
"zustand": "^5.0.2",
|
|
108
|
+
"expo-device": "~7.0.2",
|
|
109
|
+
"expo-application": "~5.9.1",
|
|
110
|
+
"expo-localization": "~16.0.1"
|
|
99
111
|
},
|
|
100
112
|
"publishConfig": {
|
|
101
113
|
"access": "public"
|
|
@@ -59,7 +59,6 @@ interface PickerModalProps {
|
|
|
59
59
|
export const PickerModal: React.FC<PickerModalProps> = React.memo(({
|
|
60
60
|
visible,
|
|
61
61
|
onClose,
|
|
62
|
-
options: _options,
|
|
63
62
|
selectedValues,
|
|
64
63
|
onSelect,
|
|
65
64
|
title,
|
|
@@ -67,7 +66,6 @@ export const PickerModal: React.FC<PickerModalProps> = React.memo(({
|
|
|
67
66
|
searchQuery,
|
|
68
67
|
onSearchChange,
|
|
69
68
|
filteredOptions,
|
|
70
|
-
multiple: _multiple = false,
|
|
71
69
|
emptyMessage = 'No options available',
|
|
72
70
|
searchPlaceholder = 'Search...',
|
|
73
71
|
closeAccessibilityLabel = 'Close picker',
|
|
@@ -76,7 +74,7 @@ export const PickerModal: React.FC<PickerModalProps> = React.memo(({
|
|
|
76
74
|
const tokens = useAppDesignTokens();
|
|
77
75
|
const insets = useSafeAreaInsets();
|
|
78
76
|
|
|
79
|
-
const modalOverlayStyles = getModalOverlayStyles(
|
|
77
|
+
const modalOverlayStyles = getModalOverlayStyles();
|
|
80
78
|
const modalContainerStyles = getModalContainerStyles(tokens, 0);
|
|
81
79
|
const modalHeaderStyles = getModalHeaderStyles(tokens);
|
|
82
80
|
const modalTitleStyles = getModalTitleStyles(tokens);
|
|
@@ -110,7 +110,7 @@ export const getPickerErrorStyles = (tokens: DesignTokens): TextStyle => ({
|
|
|
110
110
|
});
|
|
111
111
|
|
|
112
112
|
// Modal styles
|
|
113
|
-
export const getModalOverlayStyles = (
|
|
113
|
+
export const getModalOverlayStyles = (): ViewStyle => ({
|
|
114
114
|
flex: 1,
|
|
115
115
|
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
116
116
|
justifyContent: 'flex-end',
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { Dimensions } from 'react-native';
|
|
9
|
-
import { DEVICE_BREAKPOINTS, LAYOUT_CONSTANTS } from '
|
|
10
|
-
import { validateScreenDimensions } from '
|
|
9
|
+
import { DEVICE_BREAKPOINTS, LAYOUT_CONSTANTS } from '../../responsive/config';
|
|
10
|
+
import { validateScreenDimensions } from '../../responsive/validation';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Helper function for device detection with fallback
|
|
@@ -17,13 +17,13 @@ import { validateScreenDimensions } from './validation';
|
|
|
17
17
|
* @returns Operation result or fallback
|
|
18
18
|
*/
|
|
19
19
|
const withDeviceDetectionFallback = <T>(
|
|
20
|
-
operation: () => T,
|
|
21
|
-
fallback: T,
|
|
20
|
+
operation: () => T,
|
|
21
|
+
fallback: T,
|
|
22
22
|
warningMessage: string
|
|
23
23
|
): T => {
|
|
24
24
|
try {
|
|
25
25
|
return operation();
|
|
26
|
-
} catch
|
|
26
|
+
} catch {
|
|
27
27
|
if (__DEV__) {
|
|
28
28
|
console.warn(`[DeviceDetection] ${warningMessage}`);
|
|
29
29
|
}
|
|
@@ -48,11 +48,11 @@ export enum DeviceType {
|
|
|
48
48
|
*/
|
|
49
49
|
export const getScreenDimensions = () => {
|
|
50
50
|
const { width, height } = Dimensions.get('window');
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
try {
|
|
53
53
|
validateScreenDimensions(width, height);
|
|
54
54
|
return { width, height };
|
|
55
|
-
} catch
|
|
55
|
+
} catch {
|
|
56
56
|
if (__DEV__) {
|
|
57
57
|
console.warn('[getScreenDimensions] Invalid screen dimensions detected, using fallback values');
|
|
58
58
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* iPad Breakpoints and Constants
|
|
3
|
+
* Apple HIG compliant values for iPad responsive design
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* iPad specific breakpoints following Apple HIG
|
|
8
|
+
*/
|
|
9
|
+
export const IPAD_BREAKPOINTS = {
|
|
10
|
+
IPAD_MINI: 744,
|
|
11
|
+
IPAD_10_2: 810,
|
|
12
|
+
IPAD_AIR: 820,
|
|
13
|
+
IPAD_11_PRO: 834,
|
|
14
|
+
IPAD_12_9_PRO: 1024,
|
|
15
|
+
IPAD_LANDSCAPE_MINI: 1133,
|
|
16
|
+
IPAD_LANDSCAPE_AIR: 1180,
|
|
17
|
+
IPAD_LANDSCAPE_PRO: 1366,
|
|
18
|
+
} as const;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Apple HIG Touch Target Guidelines
|
|
22
|
+
*/
|
|
23
|
+
export const TOUCH_TARGETS = {
|
|
24
|
+
MINIMUM: 44,
|
|
25
|
+
RECOMMENDED: 48,
|
|
26
|
+
IPAD_RECOMMENDED: 50,
|
|
27
|
+
} as const;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Content width constraints for iPad
|
|
31
|
+
*/
|
|
32
|
+
export const CONTENT_WIDTH_CONSTRAINTS = {
|
|
33
|
+
READABLE_MAX: 672,
|
|
34
|
+
FORM_MAX: 580,
|
|
35
|
+
CARD_MAX: 400,
|
|
36
|
+
MODAL_MAX: 600,
|
|
37
|
+
PAYWALL_MAX: 540,
|
|
38
|
+
} as const;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* iPad Layout Configuration
|
|
42
|
+
*/
|
|
43
|
+
export const IPAD_LAYOUT_CONFIG = {
|
|
44
|
+
SCREEN_PADDING: 24,
|
|
45
|
+
SECTION_SPACING: 32,
|
|
46
|
+
CARD_SPACING: 20,
|
|
47
|
+
GRID_COLUMNS_PORTRAIT: 2,
|
|
48
|
+
GRID_COLUMNS_LANDSCAPE: 3,
|
|
49
|
+
GRID_GAP: 20,
|
|
50
|
+
MODAL_CORNER_RADIUS: 16,
|
|
51
|
+
SHEET_CORNER_RADIUS: 20,
|
|
52
|
+
MODAL_HORIZONTAL_MARGIN: 48,
|
|
53
|
+
FONT_SCALE: 1.1,
|
|
54
|
+
HEADING_SCALE: 1.15,
|
|
55
|
+
} as const;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* iPad Device Detection Utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Dimensions, Platform } from 'react-native';
|
|
6
|
+
import { DEVICE_BREAKPOINTS } from '../../responsive/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 '../../responsive/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
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device Detection Module - Public API
|
|
3
|
+
*
|
|
4
|
+
* Centralized device detection utilities including:
|
|
5
|
+
* - Device type detection (phone, tablet, iPad variants)
|
|
6
|
+
* - Screen dimension utilities
|
|
7
|
+
* - Layout utilities for different device types
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// Breakpoints and constants
|
|
11
|
+
export {
|
|
12
|
+
IPAD_BREAKPOINTS,
|
|
13
|
+
TOUCH_TARGETS,
|
|
14
|
+
CONTENT_WIDTH_CONSTRAINTS,
|
|
15
|
+
IPAD_LAYOUT_CONFIG,
|
|
16
|
+
} from './iPadBreakpoints';
|
|
17
|
+
|
|
18
|
+
// Device type detection
|
|
19
|
+
export {
|
|
20
|
+
DeviceType,
|
|
21
|
+
getScreenDimensions,
|
|
22
|
+
isSmallPhone,
|
|
23
|
+
isTablet,
|
|
24
|
+
isLandscape,
|
|
25
|
+
getDeviceType,
|
|
26
|
+
getSpacingMultiplier,
|
|
27
|
+
} from './deviceDetection';
|
|
28
|
+
|
|
29
|
+
// iPad-specific detection
|
|
30
|
+
export {
|
|
31
|
+
isIPad,
|
|
32
|
+
isIPadMini,
|
|
33
|
+
isIPadPro,
|
|
34
|
+
isIPadLandscape,
|
|
35
|
+
} from './iPadDetection';
|
|
36
|
+
|
|
37
|
+
// iPad layout utilities
|
|
38
|
+
export {
|
|
39
|
+
getContentMaxWidth,
|
|
40
|
+
getIPadGridColumns,
|
|
41
|
+
getTouchTargetSize,
|
|
42
|
+
getIPadScreenPadding,
|
|
43
|
+
getIPadFontScale,
|
|
44
|
+
getIPadLayoutInfo,
|
|
45
|
+
type IPadLayoutInfo,
|
|
46
|
+
} from './iPadLayoutUtils';
|
|
47
|
+
|
|
48
|
+
// iPad modal utilities
|
|
49
|
+
export {
|
|
50
|
+
getIPadModalDimensions,
|
|
51
|
+
getPaywallDimensions,
|
|
52
|
+
type ModalDimensions,
|
|
53
|
+
type PaywallDimensions,
|
|
54
|
+
} from './iPadModalUtils';
|