@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.
- package/package.json +16 -4
- package/src/atoms/AtomicBadge.tsx +121 -0
- package/src/atoms/AtomicInput.tsx +0 -1
- package/src/atoms/AtomicPicker.tsx +0 -1
- package/src/atoms/index.ts +8 -0
- 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/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 +51 -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/index.ts +4 -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/deviceDetection.ts +5 -5
- package/src/responsive/iPadBreakpoints.ts +55 -0
- package/src/responsive/iPadDetection.ts +48 -0
- package/src/responsive/iPadLayoutUtils.ts +95 -0
- package/src/responsive/iPadModalUtils.ts +98 -0
- package/src/responsive/index.ts +31 -0
- 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.2",
|
|
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"
|
|
@@ -105,4 +117,4 @@
|
|
|
105
117
|
"README.md",
|
|
106
118
|
"LICENSE"
|
|
107
119
|
]
|
|
108
|
-
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AtomicBadge Component
|
|
3
|
+
* Reusable badge for labels, status indicators, and tags
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from "react";
|
|
7
|
+
import { View, StyleSheet, type StyleProp, type ViewStyle, type TextStyle } from "react-native";
|
|
8
|
+
import { AtomicText } from "./AtomicText";
|
|
9
|
+
import { AtomicIcon, type IconName } from "./AtomicIcon";
|
|
10
|
+
import { useAppDesignTokens } from "../theme";
|
|
11
|
+
|
|
12
|
+
export type BadgeVariant = "primary" | "secondary" | "success" | "warning" | "error" | "info";
|
|
13
|
+
export type BadgeSize = "sm" | "md" | "lg";
|
|
14
|
+
|
|
15
|
+
export interface AtomicBadgeProps {
|
|
16
|
+
/** Badge text content */
|
|
17
|
+
text: string;
|
|
18
|
+
/** Visual variant */
|
|
19
|
+
variant?: BadgeVariant;
|
|
20
|
+
/** Size preset */
|
|
21
|
+
size?: BadgeSize;
|
|
22
|
+
/** Optional icon name (Ionicons) */
|
|
23
|
+
icon?: IconName;
|
|
24
|
+
/** Icon position */
|
|
25
|
+
iconPosition?: "left" | "right";
|
|
26
|
+
/** Custom container style */
|
|
27
|
+
style?: StyleProp<ViewStyle>;
|
|
28
|
+
/** Custom text style */
|
|
29
|
+
textStyle?: StyleProp<TextStyle>;
|
|
30
|
+
/** Test ID for testing */
|
|
31
|
+
testID?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const SIZE_CONFIG = {
|
|
35
|
+
sm: { paddingH: 6, paddingV: 2, fontSize: 10, iconSize: 10, gap: 3, radius: 4 },
|
|
36
|
+
md: { paddingH: 8, paddingV: 4, fontSize: 11, iconSize: 12, gap: 4, radius: 6 },
|
|
37
|
+
lg: { paddingH: 12, paddingV: 6, fontSize: 13, iconSize: 14, gap: 5, radius: 8 },
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const AtomicBadge: React.FC<AtomicBadgeProps> = React.memo(({
|
|
41
|
+
text,
|
|
42
|
+
variant = "primary",
|
|
43
|
+
size = "md",
|
|
44
|
+
icon,
|
|
45
|
+
iconPosition = "left",
|
|
46
|
+
style,
|
|
47
|
+
textStyle,
|
|
48
|
+
testID,
|
|
49
|
+
}) => {
|
|
50
|
+
const tokens = useAppDesignTokens();
|
|
51
|
+
const sizeConfig = SIZE_CONFIG[size];
|
|
52
|
+
|
|
53
|
+
const getVariantColors = () => {
|
|
54
|
+
switch (variant) {
|
|
55
|
+
case "primary":
|
|
56
|
+
return { bg: tokens.colors.primaryLight, text: tokens.colors.primary };
|
|
57
|
+
case "secondary":
|
|
58
|
+
return { bg: tokens.colors.surfaceSecondary, text: tokens.colors.textSecondary };
|
|
59
|
+
case "success":
|
|
60
|
+
return { bg: tokens.colors.successLight, text: tokens.colors.success };
|
|
61
|
+
case "warning":
|
|
62
|
+
return { bg: tokens.colors.warningLight, text: tokens.colors.warning };
|
|
63
|
+
case "error":
|
|
64
|
+
return { bg: tokens.colors.errorLight, text: tokens.colors.error };
|
|
65
|
+
case "info":
|
|
66
|
+
return { bg: tokens.colors.infoLight, text: tokens.colors.info };
|
|
67
|
+
default:
|
|
68
|
+
return { bg: tokens.colors.primaryLight, text: tokens.colors.primary };
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const colors = getVariantColors();
|
|
73
|
+
|
|
74
|
+
const containerStyle: StyleProp<ViewStyle> = [
|
|
75
|
+
styles.container,
|
|
76
|
+
{
|
|
77
|
+
backgroundColor: colors.bg,
|
|
78
|
+
paddingHorizontal: sizeConfig.paddingH,
|
|
79
|
+
paddingVertical: sizeConfig.paddingV,
|
|
80
|
+
borderRadius: sizeConfig.radius,
|
|
81
|
+
gap: sizeConfig.gap,
|
|
82
|
+
flexDirection: iconPosition === "right" ? "row-reverse" : "row",
|
|
83
|
+
},
|
|
84
|
+
style,
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<View style={containerStyle} testID={testID}>
|
|
89
|
+
{icon && (
|
|
90
|
+
<AtomicIcon
|
|
91
|
+
name={icon}
|
|
92
|
+
customSize={sizeConfig.iconSize}
|
|
93
|
+
customColor={colors.text}
|
|
94
|
+
/>
|
|
95
|
+
)}
|
|
96
|
+
<AtomicText
|
|
97
|
+
type="labelSmall"
|
|
98
|
+
style={[
|
|
99
|
+
{
|
|
100
|
+
color: colors.text,
|
|
101
|
+
fontSize: sizeConfig.fontSize,
|
|
102
|
+
fontWeight: "700",
|
|
103
|
+
},
|
|
104
|
+
textStyle,
|
|
105
|
+
]}
|
|
106
|
+
>
|
|
107
|
+
{text}
|
|
108
|
+
</AtomicText>
|
|
109
|
+
</View>
|
|
110
|
+
);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
AtomicBadge.displayName = "AtomicBadge";
|
|
114
|
+
|
|
115
|
+
const styles = StyleSheet.create({
|
|
116
|
+
container: {
|
|
117
|
+
alignSelf: "flex-start",
|
|
118
|
+
alignItems: "center",
|
|
119
|
+
justifyContent: "center",
|
|
120
|
+
},
|
|
121
|
+
});
|
package/src/atoms/index.ts
CHANGED
|
@@ -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',
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device Domain - Core Entities
|
|
3
|
+
*
|
|
4
|
+
* This file defines core types and interfaces for device information.
|
|
5
|
+
* Handles device details, app info using expo-device and expo-application.
|
|
6
|
+
*
|
|
7
|
+
* @domain device
|
|
8
|
+
* @layer domain/entities
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type * as DeviceModule from 'expo-device';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Re-export Device types from expo-device
|
|
15
|
+
*/
|
|
16
|
+
export type DeviceType = DeviceModule.DeviceType;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Device information interface
|
|
20
|
+
*/
|
|
21
|
+
export interface DeviceInfo {
|
|
22
|
+
// Device identification
|
|
23
|
+
brand: string | null;
|
|
24
|
+
manufacturer: string | null;
|
|
25
|
+
modelName: string | null;
|
|
26
|
+
modelId: string | null;
|
|
27
|
+
deviceName: string | null;
|
|
28
|
+
deviceYearClass: number | null;
|
|
29
|
+
|
|
30
|
+
// Device type
|
|
31
|
+
deviceType: DeviceType | null;
|
|
32
|
+
isDevice: boolean;
|
|
33
|
+
|
|
34
|
+
// OS information
|
|
35
|
+
osName: string | null;
|
|
36
|
+
osVersion: string | null;
|
|
37
|
+
osBuildId: string | null;
|
|
38
|
+
platformApiLevel: number | null;
|
|
39
|
+
|
|
40
|
+
// Memory
|
|
41
|
+
totalMemory: number | null;
|
|
42
|
+
|
|
43
|
+
// Platform
|
|
44
|
+
platform: 'ios' | 'android' | 'web';
|
|
45
|
+
|
|
46
|
+
// Localization
|
|
47
|
+
timezone: string | null;
|
|
48
|
+
region: string | null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Application information interface
|
|
53
|
+
*/
|
|
54
|
+
export interface ApplicationInfo {
|
|
55
|
+
// App identification
|
|
56
|
+
applicationName: string;
|
|
57
|
+
applicationId: string;
|
|
58
|
+
nativeApplicationVersion: string | null;
|
|
59
|
+
nativeBuildVersion: string | null;
|
|
60
|
+
|
|
61
|
+
// Installation
|
|
62
|
+
installTime: Date | null;
|
|
63
|
+
lastUpdateTime: Date | null;
|
|
64
|
+
|
|
65
|
+
// Platform-specific
|
|
66
|
+
androidId: string | null; // Android only
|
|
67
|
+
iosIdForVendor: string | null; // iOS only
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Combined device and app info
|
|
72
|
+
*/
|
|
73
|
+
export interface SystemInfo {
|
|
74
|
+
device: DeviceInfo;
|
|
75
|
+
application: ApplicationInfo;
|
|
76
|
+
timestamp: number;
|
|
77
|
+
userId?: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Device constants
|
|
82
|
+
*/
|
|
83
|
+
export const DEVICE_CONSTANTS = {
|
|
84
|
+
DEVICE_TYPE: {
|
|
85
|
+
UNKNOWN: 0,
|
|
86
|
+
PHONE: 1,
|
|
87
|
+
TABLET: 2,
|
|
88
|
+
DESKTOP: 3,
|
|
89
|
+
TV: 4,
|
|
90
|
+
},
|
|
91
|
+
PLATFORM: {
|
|
92
|
+
IOS: 'ios',
|
|
93
|
+
ANDROID: 'android',
|
|
94
|
+
WEB: 'web',
|
|
95
|
+
},
|
|
96
|
+
} as const;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Device utilities
|
|
100
|
+
*/
|
|
101
|
+
export class DeviceUtils {
|
|
102
|
+
/**
|
|
103
|
+
* Check if running on physical device (not simulator/emulator)
|
|
104
|
+
*/
|
|
105
|
+
static isPhysicalDevice(isDevice: boolean): boolean {
|
|
106
|
+
return isDevice;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get device display name
|
|
111
|
+
*/
|
|
112
|
+
static getDeviceDisplayName(info: DeviceInfo): string {
|
|
113
|
+
if (info.deviceName) {
|
|
114
|
+
return info.deviceName;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (info.modelName) {
|
|
118
|
+
return info.modelName;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (info.brand && info.manufacturer) {
|
|
122
|
+
return `${info.brand} ${info.manufacturer}`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return 'Unknown Device';
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Get OS display string
|
|
130
|
+
*/
|
|
131
|
+
static getOSDisplayString(info: DeviceInfo): string {
|
|
132
|
+
if (info.osName && info.osVersion) {
|
|
133
|
+
return `${info.osName} ${info.osVersion}`;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (info.osName) {
|
|
137
|
+
return info.osName;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return 'Unknown OS';
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Get app version string
|
|
145
|
+
*/
|
|
146
|
+
static getAppVersionString(info: ApplicationInfo): string {
|
|
147
|
+
if (info.nativeApplicationVersion && info.nativeBuildVersion) {
|
|
148
|
+
return `${info.nativeApplicationVersion} (${info.nativeBuildVersion})`;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (info.nativeApplicationVersion) {
|
|
152
|
+
return info.nativeApplicationVersion;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return 'Unknown Version';
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Check if device meets minimum requirements
|
|
160
|
+
*/
|
|
161
|
+
static meetsMinimumRequirements(info: DeviceInfo, minMemoryGB: number = 1): {
|
|
162
|
+
meets: boolean;
|
|
163
|
+
reasons: string[];
|
|
164
|
+
} {
|
|
165
|
+
const reasons: string[] = [];
|
|
166
|
+
|
|
167
|
+
if (!info.isDevice) {
|
|
168
|
+
reasons.push('Running on simulator/emulator');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (info.totalMemory) {
|
|
172
|
+
const memoryGB = info.totalMemory / (1024 * 1024 * 1024);
|
|
173
|
+
if (memoryGB < minMemoryGB) {
|
|
174
|
+
reasons.push(`Insufficient memory: ${memoryGB.toFixed(2)}GB (minimum: ${minMemoryGB}GB)`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (info.deviceYearClass && info.deviceYearClass < 2018) {
|
|
179
|
+
reasons.push(`Device too old: ${info.deviceYearClass} (minimum: 2018)`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
meets: reasons.length === 0,
|
|
184
|
+
reasons,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Get device tier (low/mid/high) based on specs
|
|
190
|
+
*/
|
|
191
|
+
static getDeviceTier(info: DeviceInfo): 'low' | 'mid' | 'high' {
|
|
192
|
+
if (info.deviceYearClass) {
|
|
193
|
+
if (info.deviceYearClass >= 2022) return 'high';
|
|
194
|
+
if (info.deviceYearClass >= 2019) return 'mid';
|
|
195
|
+
return 'low';
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (info.totalMemory) {
|
|
199
|
+
const memoryGB = info.totalMemory / (1024 * 1024 * 1024);
|
|
200
|
+
if (memoryGB >= 6) return 'high';
|
|
201
|
+
if (memoryGB >= 3) return 'mid';
|
|
202
|
+
return 'low';
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return 'mid';
|
|
206
|
+
}
|
|
207
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device Memory Utilities
|
|
3
|
+
*
|
|
4
|
+
* Utility functions for device memory calculations and formatting
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Device memory utilities
|
|
9
|
+
*/
|
|
10
|
+
export class DeviceMemoryUtils {
|
|
11
|
+
/**
|
|
12
|
+
* Convert bytes to gigabytes
|
|
13
|
+
*/
|
|
14
|
+
static bytesToGB(bytes: number): number {
|
|
15
|
+
return bytes / (1024 * 1024 * 1024);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Convert bytes to megabytes
|
|
20
|
+
*/
|
|
21
|
+
static bytesToMB(bytes: number): number {
|
|
22
|
+
return bytes / (1024 * 1024);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Format memory size to human readable string
|
|
27
|
+
*/
|
|
28
|
+
static formatMemorySize(bytes: number | null): string {
|
|
29
|
+
if (!bytes) return 'Unknown';
|
|
30
|
+
|
|
31
|
+
const gb = this.bytesToGB(bytes);
|
|
32
|
+
if (gb >= 1) {
|
|
33
|
+
return `${gb.toFixed(2)} GB`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const mb = this.bytesToMB(bytes);
|
|
37
|
+
return `${mb.toFixed(2)} MB`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Check if memory is sufficient for requirements
|
|
42
|
+
*/
|
|
43
|
+
static hasSufficientMemory(
|
|
44
|
+
totalMemory: number | null,
|
|
45
|
+
requiredGB: number
|
|
46
|
+
): boolean {
|
|
47
|
+
if (!totalMemory) return false;
|
|
48
|
+
return this.bytesToGB(totalMemory) >= requiredGB;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get memory tier classification
|
|
53
|
+
*/
|
|
54
|
+
static getMemoryTier(totalMemory: number | null): 'low' | 'mid' | 'high' {
|
|
55
|
+
if (!totalMemory) return 'mid';
|
|
56
|
+
|
|
57
|
+
const gb = this.bytesToGB(totalMemory);
|
|
58
|
+
if (gb >= 6) return 'high';
|
|
59
|
+
if (gb >= 3) return 'mid';
|
|
60
|
+
return 'low';
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device Type Utilities
|
|
3
|
+
*
|
|
4
|
+
* Utility functions for device type detection
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { DeviceType } from './Device';
|
|
8
|
+
import { DEVICE_CONSTANTS } from './Device';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Device type utilities
|
|
12
|
+
*/
|
|
13
|
+
export class DeviceTypeUtils {
|
|
14
|
+
/**
|
|
15
|
+
* Check if device is a tablet
|
|
16
|
+
*/
|
|
17
|
+
static isTablet(deviceType: DeviceType | null): boolean {
|
|
18
|
+
return deviceType === DEVICE_CONSTANTS.DEVICE_TYPE.TABLET;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Check if device is a phone
|
|
23
|
+
*/
|
|
24
|
+
static isPhone(deviceType: DeviceType | null): boolean {
|
|
25
|
+
return deviceType === DEVICE_CONSTANTS.DEVICE_TYPE.PHONE;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Check if device is desktop
|
|
30
|
+
*/
|
|
31
|
+
static isDesktop(deviceType: DeviceType | null): boolean {
|
|
32
|
+
return deviceType === DEVICE_CONSTANTS.DEVICE_TYPE.DESKTOP;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Check if device is TV
|
|
37
|
+
*/
|
|
38
|
+
static isTV(deviceType: DeviceType | null): boolean {
|
|
39
|
+
return deviceType === DEVICE_CONSTANTS.DEVICE_TYPE.TV;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Check if device type is unknown
|
|
44
|
+
*/
|
|
45
|
+
static isUnknown(deviceType: DeviceType | null): boolean {
|
|
46
|
+
return deviceType === DEVICE_CONSTANTS.DEVICE_TYPE.UNKNOWN;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Get device type name
|
|
51
|
+
*/
|
|
52
|
+
static getDeviceTypeName(deviceType: DeviceType | null): string {
|
|
53
|
+
switch (deviceType) {
|
|
54
|
+
case DEVICE_CONSTANTS.DEVICE_TYPE.PHONE:
|
|
55
|
+
return 'Phone';
|
|
56
|
+
case DEVICE_CONSTANTS.DEVICE_TYPE.TABLET:
|
|
57
|
+
return 'Tablet';
|
|
58
|
+
case DEVICE_CONSTANTS.DEVICE_TYPE.DESKTOP:
|
|
59
|
+
return 'Desktop';
|
|
60
|
+
case DEVICE_CONSTANTS.DEVICE_TYPE.TV:
|
|
61
|
+
return 'TV';
|
|
62
|
+
default:
|
|
63
|
+
return 'Unknown';
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|