@umituz/react-native-design-system 2.6.15 → 2.6.17

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-design-system",
3
- "version": "2.6.15",
3
+ "version": "2.6.17",
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",
@@ -24,12 +24,11 @@
24
24
  */
25
25
 
26
26
  import React, { useMemo } from 'react';
27
- import { View, ScrollView, StyleSheet, type ViewStyle, RefreshControlProps } from 'react-native';
27
+ import { View, ScrollView, StyleSheet, KeyboardAvoidingView, type ViewStyle, type RefreshControlProps } from 'react-native';
28
28
  import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
29
29
  import type { Edge } from 'react-native-safe-area-context';
30
30
  import { useAppDesignTokens } from '../../theme';
31
- import { getResponsiveHorizontalPadding } from '../../responsive/responsiveLayout';
32
- import { isTablet as checkIsTablet } from '../../device/detection';
31
+ import { getScreenLayoutConfig } from '../../responsive/responsiveLayout';
33
32
 
34
33
  /**
35
34
  * NOTE: This component now works in conjunction with the SafeAreaProvider
@@ -161,16 +160,25 @@ export const ScreenLayout: React.FC<ScreenLayoutProps> = ({
161
160
  // Automatically uses current theme from global store
162
161
  const tokens = useAppDesignTokens();
163
162
  const insets = useSafeAreaInsets();
164
- const isTabletDevice = checkIsTablet();
165
163
 
166
- // Only apply maxWidth for tablets - phones should fill full width
167
- const finalMaxWidth = maxWidth || (responsiveEnabled && isTabletDevice ? 600 : undefined);
168
- const horizontalPadding = responsiveEnabled ? getResponsiveHorizontalPadding(tokens.spacing.md, insets) : tokens.spacing.md;
164
+ // Get all responsive layout values from centralized config
165
+ const layoutConfig = useMemo(
166
+ () => getScreenLayoutConfig(insets),
167
+ [insets]
168
+ );
169
+
170
+ // Use provided maxWidth or responsive default
171
+ const finalMaxWidth = maxWidth || (responsiveEnabled ? layoutConfig.maxContentWidth : undefined);
172
+ const horizontalPadding = responsiveEnabled ? layoutConfig.horizontalPadding : tokens.spacing.md;
173
+ const verticalPadding = responsiveEnabled ? layoutConfig.verticalPadding : tokens.spacing.lg;
169
174
 
170
175
  const styles = useMemo(() => StyleSheet.create({
171
176
  container: {
172
177
  flex: 1,
173
178
  },
179
+ keyboardAvoidingView: {
180
+ flex: 1,
181
+ },
174
182
  responsiveWrapper: {
175
183
  flex: 1,
176
184
  width: '100%',
@@ -178,6 +186,7 @@ export const ScreenLayout: React.FC<ScreenLayoutProps> = ({
178
186
  },
179
187
  content: {
180
188
  flex: 1,
189
+ paddingTop: verticalPadding,
181
190
  paddingHorizontal: horizontalPadding,
182
191
  },
183
192
  scrollView: {
@@ -185,13 +194,30 @@ export const ScreenLayout: React.FC<ScreenLayoutProps> = ({
185
194
  },
186
195
  scrollContent: {
187
196
  flexGrow: 1,
197
+ paddingTop: verticalPadding,
188
198
  paddingHorizontal: horizontalPadding,
189
- paddingBottom: tokens.spacing.lg,
199
+ paddingBottom: verticalPadding,
190
200
  },
191
- }), [tokens, finalMaxWidth, horizontalPadding]);
201
+ }), [tokens, finalMaxWidth, horizontalPadding, verticalPadding]);
192
202
 
193
203
  const bgColor = backgroundColor || tokens.colors.backgroundPrimary;
194
204
 
205
+ // Content wrapper - optionally with KeyboardAvoidingView
206
+ // Uses 'padding' behavior which works consistently cross-platform
207
+ const ContentWrapper: React.FC<{ children: React.ReactNode }> = ({ children: wrapperChildren }) => {
208
+ if (keyboardAvoiding) {
209
+ return (
210
+ <KeyboardAvoidingView
211
+ style={styles.keyboardAvoidingView}
212
+ behavior="padding"
213
+ >
214
+ {wrapperChildren}
215
+ </KeyboardAvoidingView>
216
+ );
217
+ }
218
+ return <>{wrapperChildren}</>;
219
+ };
220
+
195
221
  // Non-scrollable layout
196
222
  if (!scrollable) {
197
223
  return (
@@ -200,13 +226,15 @@ export const ScreenLayout: React.FC<ScreenLayoutProps> = ({
200
226
  edges={edges}
201
227
  testID={testID}
202
228
  >
203
- <View style={styles.responsiveWrapper}>
204
- {header}
205
- <View style={[styles.content, contentContainerStyle]}>
206
- {children}
229
+ <ContentWrapper>
230
+ <View style={styles.responsiveWrapper}>
231
+ {header}
232
+ <View style={[styles.content, contentContainerStyle]}>
233
+ {children}
234
+ </View>
235
+ {footer}
207
236
  </View>
208
- {footer}
209
- </View>
237
+ </ContentWrapper>
210
238
  </SafeAreaView>
211
239
  );
212
240
  }
@@ -218,19 +246,21 @@ export const ScreenLayout: React.FC<ScreenLayoutProps> = ({
218
246
  edges={edges}
219
247
  testID={testID}
220
248
  >
221
- <View style={styles.responsiveWrapper}>
222
- {header}
223
- <ScrollView
224
- style={styles.scrollView}
225
- contentContainerStyle={[styles.scrollContent, contentContainerStyle]}
226
- showsVerticalScrollIndicator={!hideScrollIndicator}
227
- keyboardShouldPersistTaps={keyboardAvoiding ? 'handled' : 'never'}
228
- refreshControl={refreshControl}
229
- >
230
- {children}
231
- </ScrollView>
232
- {footer}
233
- </View>
249
+ <ContentWrapper>
250
+ <View style={styles.responsiveWrapper}>
251
+ {header}
252
+ <ScrollView
253
+ style={styles.scrollView}
254
+ contentContainerStyle={[styles.scrollContent, contentContainerStyle]}
255
+ showsVerticalScrollIndicator={!hideScrollIndicator}
256
+ keyboardShouldPersistTaps={keyboardAvoiding ? 'handled' : 'never'}
257
+ refreshControl={refreshControl}
258
+ >
259
+ {children}
260
+ </ScrollView>
261
+ {footer}
262
+ </View>
263
+ </ContentWrapper>
234
264
  </SafeAreaView>
235
265
  );
236
266
  };
@@ -1,6 +1,7 @@
1
- import { Platform } from 'react-native';
2
1
  import { useMemo } from 'react';
2
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
3
3
  import { useAppDesignTokens } from '../../../theme';
4
+ import { getResponsiveTabBarConfig } from '../../../responsive/responsiveLayout';
4
5
 
5
6
  export interface TabBarConfig {
6
7
  backgroundColor?: string;
@@ -19,17 +20,24 @@ export interface TabBarConfig {
19
20
 
20
21
  export function useTabBarStyles(config: TabBarConfig = {}) {
21
22
  const tokens = useAppDesignTokens();
23
+ const insets = useSafeAreaInsets();
24
+
25
+ // Get responsive tab bar config based on device type and safe area
26
+ const responsiveConfig = useMemo(
27
+ () => getResponsiveTabBarConfig(insets),
28
+ [insets]
29
+ );
22
30
 
23
31
  const tabBarStyle = useMemo(() => ({
24
32
  backgroundColor: config.backgroundColor || tokens.colors.surface,
25
33
  borderTopColor: config.borderTopColor || tokens.colors.borderLight,
26
34
  borderTopWidth: config.borderTopWidth ?? 1,
27
- paddingTop: config.paddingTop ?? 12,
28
- paddingBottom: config.paddingBottom ?? (Platform.OS === 'ios' ? 24 : 12),
29
- minHeight: config.minHeight ?? (Platform.OS === 'ios' ? 80 : 70),
35
+ paddingTop: config.paddingTop ?? responsiveConfig.paddingTop,
36
+ paddingBottom: config.paddingBottom ?? responsiveConfig.paddingBottom,
37
+ minHeight: config.minHeight ?? responsiveConfig.height,
30
38
  }), [config.backgroundColor, config.borderTopColor, config.borderTopWidth,
31
39
  config.paddingTop, config.paddingBottom, config.minHeight, tokens.colors.surface,
32
- tokens.colors.borderLight]);
40
+ tokens.colors.borderLight, responsiveConfig]);
33
41
 
34
42
  const screenOptions = useMemo(() => ({
35
43
  headerShown: false,
@@ -91,6 +91,11 @@ export const MODAL_CONFIG = {
91
91
  HEIGHT_PERCENT_STANDARD: 0.78, // 78% of screen height for standard devices
92
92
  HEIGHT_PERCENT_TABLET: 0.70, // 70% of screen height for tablets
93
93
 
94
+ // Min modal height percentages (for calculated min heights)
95
+ MIN_HEIGHT_PERCENT_SMALL: 0.40, // 40% of screen height for small devices
96
+ MIN_HEIGHT_PERCENT_STANDARD: 0.45, // 45% of screen height for standard devices
97
+ MIN_HEIGHT_PERCENT_TABLET: 0.35, // 35% of screen height for tablets
98
+
94
99
  // Max width constraints
95
100
  MAX_WIDTH_PHONE: 480, // Maximum modal width for phones
96
101
  MAX_WIDTH_TABLET: 600, // Maximum modal width for tablets
@@ -121,9 +126,12 @@ export const LAYOUT_CONSTANTS = {
121
126
  SPACING_MULTIPLIER_SMALL: 0.90, // 90% spacing for small devices
122
127
  SPACING_MULTIPLIER_TABLET: 1.20, // 120% spacing for tablets
123
128
  SPACING_MULTIPLIER_STANDARD: 1.0, // 100% spacing for standard devices
124
-
129
+
125
130
  // Padding and margins
126
131
  HORIZONTAL_PADDING_BASE: 16, // Base horizontal padding
132
+ VERTICAL_PADDING_SMALL: 12, // Vertical padding for small devices
133
+ VERTICAL_PADDING_STANDARD: 16, // Vertical padding for standard devices
134
+ VERTICAL_PADDING_TABLET: 24, // Vertical padding for tablets
127
135
  BOTTOM_POSITION_BASE: 32, // Base bottom position
128
136
 
129
137
  // Safe area offsets
@@ -14,11 +14,14 @@ export {
14
14
  getResponsiveLogoSize,
15
15
  getResponsiveInputHeight,
16
16
  getResponsiveHorizontalPadding,
17
+ getResponsiveVerticalPadding,
18
+ getScreenLayoutConfig,
17
19
  getResponsiveBottomPosition,
18
20
  getResponsiveFABPosition,
19
21
  getResponsiveTabBarHeight,
20
22
  getResponsiveTabBarConfig,
21
23
  type ResponsiveTabBarConfig,
24
+ type ScreenLayoutConfig,
22
25
  getResponsiveModalMaxHeight,
23
26
  getResponsiveMinModalHeight,
24
27
  getResponsiveModalWidth,
@@ -19,11 +19,14 @@ export {
19
19
  // Responsive layout
20
20
  export {
21
21
  getResponsiveHorizontalPadding,
22
+ getResponsiveVerticalPadding,
23
+ getScreenLayoutConfig,
22
24
  getResponsiveBottomPosition,
23
25
  getResponsiveFABPosition,
24
26
  getResponsiveTabBarHeight,
25
27
  getResponsiveTabBarConfig,
26
28
  type ResponsiveTabBarConfig,
29
+ type ScreenLayoutConfig,
27
30
  } from './responsiveLayout';
28
31
 
29
32
  // Responsive modal utilities
@@ -3,8 +3,8 @@
3
3
  * Layout utilities for positioning and spacing.
4
4
  */
5
5
 
6
- import { getScreenDimensions, isTablet } from '../device/detection';
7
- import { LAYOUT_CONSTANTS } from './config';
6
+ import { isTablet, isSmallPhone, getSpacingMultiplier } from '../device/detection';
7
+ import { LAYOUT_CONSTANTS, SIZE_CONSTRAINTS } from './config';
8
8
  import { validateNumber, validateSafeAreaInsets } from './validation';
9
9
 
10
10
  /**
@@ -13,6 +13,75 @@ import { validateNumber, validateSafeAreaInsets } from './validation';
13
13
  */
14
14
  const checkIsTabletSize = (): boolean => isTablet();
15
15
 
16
+ /**
17
+ * Screen layout configuration for ScreenLayout component
18
+ */
19
+ export interface ScreenLayoutConfig {
20
+ maxContentWidth: number | undefined;
21
+ horizontalPadding: number;
22
+ verticalPadding: number;
23
+ spacingMultiplier: number;
24
+ }
25
+
26
+ /**
27
+ * Get complete screen layout configuration
28
+ * Returns all responsive values needed for ScreenLayout
29
+ */
30
+ export const getScreenLayoutConfig = (
31
+ insets: { left?: number; right?: number; top?: number; bottom?: number } = {}
32
+ ): ScreenLayoutConfig => {
33
+ try {
34
+ const isTabletDevice = checkIsTabletSize();
35
+ const spacingMultiplier = getSpacingMultiplier();
36
+
37
+ return {
38
+ maxContentWidth: isTabletDevice ? SIZE_CONSTRAINTS.CONTENT_MAX_TABLET : undefined,
39
+ horizontalPadding: getResponsiveHorizontalPadding(LAYOUT_CONSTANTS.HORIZONTAL_PADDING_BASE, insets),
40
+ verticalPadding: getResponsiveVerticalPadding(insets),
41
+ spacingMultiplier,
42
+ };
43
+ } catch {
44
+ return {
45
+ maxContentWidth: undefined,
46
+ horizontalPadding: LAYOUT_CONSTANTS.HORIZONTAL_PADDING_BASE,
47
+ verticalPadding: LAYOUT_CONSTANTS.VERTICAL_PADDING_STANDARD,
48
+ spacingMultiplier: LAYOUT_CONSTANTS.SPACING_MULTIPLIER_STANDARD,
49
+ };
50
+ }
51
+ };
52
+
53
+ /**
54
+ * Responsive vertical padding
55
+ * Adjusts based on device type and safe area insets
56
+ */
57
+ export const getResponsiveVerticalPadding = (
58
+ insets: { top?: number; bottom?: number } = { top: 0, bottom: 0 }
59
+ ): number => {
60
+ try {
61
+ validateSafeAreaInsets(insets);
62
+ const { top = 0 } = insets;
63
+ const isTabletDevice = checkIsTabletSize();
64
+ const isSmall = isSmallPhone();
65
+ const spacingMultiplier = getSpacingMultiplier();
66
+
67
+ // Base padding adjusted by device type
68
+ let basePadding: number = LAYOUT_CONSTANTS.VERTICAL_PADDING_STANDARD;
69
+ if (isTabletDevice) {
70
+ basePadding = LAYOUT_CONSTANTS.VERTICAL_PADDING_TABLET;
71
+ } else if (isSmall) {
72
+ basePadding = LAYOUT_CONSTANTS.VERTICAL_PADDING_SMALL;
73
+ }
74
+
75
+ // Apply spacing multiplier for consistency
76
+ const adjustedPadding = basePadding * spacingMultiplier;
77
+
78
+ // Ensure minimum padding respects safe area
79
+ return Math.max(adjustedPadding, top > 0 ? 8 : adjustedPadding);
80
+ } catch {
81
+ return LAYOUT_CONSTANTS.VERTICAL_PADDING_STANDARD;
82
+ }
83
+ };
84
+
16
85
  /**
17
86
  * Responsive horizontal padding
18
87
  */
@@ -28,7 +97,7 @@ export const getResponsiveHorizontalPadding = (
28
97
  const isTabletDevice = checkIsTabletSize();
29
98
 
30
99
  if (isTabletDevice) {
31
- const tabletPadding = validatedBasePadding * 1.5;
100
+ const tabletPadding = validatedBasePadding * LAYOUT_CONSTANTS.SPACING_MULTIPLIER_TABLET;
32
101
  return Math.max(
33
102
  tabletPadding,
34
103
  left + LAYOUT_CONSTANTS.HORIZONTAL_PADDING_BASE,
@@ -99,7 +168,10 @@ export const getResponsiveFABPosition = (
99
168
  ),
100
169
  };
101
170
  } catch {
102
- return { bottom: 90, right: 20 };
171
+ return {
172
+ bottom: LAYOUT_CONSTANTS.TAB_BAR_OFFSET,
173
+ right: LAYOUT_CONSTANTS.FAB_RIGHT_PHONE,
174
+ };
103
175
  }
104
176
  };
105
177
 
@@ -53,17 +53,17 @@ export const getResponsiveMinModalHeight = (): number => {
53
53
  const { height } = getScreenDimensions();
54
54
 
55
55
  if (height <= HEIGHT_THRESHOLDS.SMALL_DEVICE) {
56
- const calculatedHeight = height * 0.4;
56
+ const calculatedHeight = height * MODAL_CONFIG.MIN_HEIGHT_PERCENT_SMALL;
57
57
  return Math.max(calculatedHeight, SIZE_CONSTRAINTS.MODAL_MIN_SMALL);
58
58
  } else if (height >= HEIGHT_THRESHOLDS.LARGE_DEVICE) {
59
- const calculatedHeight = height * 0.35;
59
+ const calculatedHeight = height * MODAL_CONFIG.MIN_HEIGHT_PERCENT_TABLET;
60
60
  return Math.min(
61
61
  Math.max(calculatedHeight, SIZE_CONSTRAINTS.MODAL_MIN_TABLET),
62
62
  SIZE_CONSTRAINTS.MODAL_MAX_TABLET
63
63
  );
64
64
  }
65
65
 
66
- const calculatedHeight = height * 0.45;
66
+ const calculatedHeight = height * MODAL_CONFIG.MIN_HEIGHT_PERCENT_STANDARD;
67
67
  return Math.max(calculatedHeight, SIZE_CONSTRAINTS.MODAL_MIN_STANDARD);
68
68
  } catch {
69
69
  return 300;
@@ -146,7 +146,7 @@ export const getResponsiveModalLayout = (): ResponsiveModalLayout => {
146
146
  borderRadius: getResponsiveModalBorderRadius(),
147
147
  backdropOpacity: getResponsiveBackdropOpacity(),
148
148
  horizontalPadding: isTabletDevice
149
- ? LAYOUT_CONSTANTS.HORIZONTAL_PADDING_BASE * 1.5
149
+ ? LAYOUT_CONSTANTS.HORIZONTAL_PADDING_BASE * LAYOUT_CONSTANTS.SPACING_MULTIPLIER_TABLET
150
150
  : LAYOUT_CONSTANTS.HORIZONTAL_PADDING_BASE,
151
151
  };
152
152
  };
@@ -17,6 +17,8 @@ import {
17
17
  getResponsiveLogoSize,
18
18
  getResponsiveInputHeight,
19
19
  getResponsiveHorizontalPadding,
20
+ getResponsiveVerticalPadding,
21
+ getScreenLayoutConfig,
20
22
  getResponsiveBottomPosition,
21
23
  getResponsiveFABPosition,
22
24
  getResponsiveTabBarConfig,
@@ -33,6 +35,7 @@ import {
33
35
  type ResponsiveBottomSheetLayout,
34
36
  type ResponsiveDialogLayout,
35
37
  type ResponsiveTabBarConfig,
38
+ type ScreenLayoutConfig,
36
39
  } from "./responsive";
37
40
  import {
38
41
  isSmallPhone,
@@ -70,9 +73,13 @@ export interface UseResponsiveReturn {
70
73
 
71
74
  // Responsive positioning
72
75
  horizontalPadding: number;
76
+ verticalPadding: number;
73
77
  bottomPosition: number;
74
78
  fabPosition: { bottom: number; right: number };
75
79
 
80
+ // Screen layout config (complete configuration for ScreenLayout)
81
+ screenLayoutConfig: ScreenLayoutConfig;
82
+
76
83
  // Responsive layout
77
84
  modalMaxHeight: string;
78
85
  modalMinHeight: number;
@@ -160,9 +167,13 @@ export const useResponsive = (): UseResponsiveReturn => {
160
167
 
161
168
  // Responsive positioning
162
169
  horizontalPadding: getResponsiveHorizontalPadding(undefined, insets),
170
+ verticalPadding: getResponsiveVerticalPadding(insets),
163
171
  bottomPosition: getResponsiveBottomPosition(undefined, insets),
164
172
  fabPosition: getResponsiveFABPosition(insets),
165
173
 
174
+ // Screen layout config (complete configuration for ScreenLayout)
175
+ screenLayoutConfig: getScreenLayoutConfig(insets),
176
+
166
177
  // Responsive layout
167
178
  modalMaxHeight: getResponsiveModalMaxHeight(),
168
179
  modalMinHeight: getResponsiveMinModalHeight(),