@umituz/react-native-design-system 1.15.0 → 2.0.1

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 (88) hide show
  1. package/package.json +26 -19
  2. package/src/atoms/AtomicAvatar.tsx +161 -0
  3. package/src/atoms/AtomicButton.tsx +241 -0
  4. package/src/atoms/AtomicCard.tsx +84 -0
  5. package/src/atoms/AtomicChip.tsx +226 -0
  6. package/src/atoms/AtomicDatePicker.tsx +255 -0
  7. package/src/atoms/AtomicFab.tsx +99 -0
  8. package/src/atoms/AtomicIcon.tsx +149 -0
  9. package/src/atoms/AtomicInput.tsx +308 -0
  10. package/src/atoms/AtomicPicker.tsx +310 -0
  11. package/src/atoms/AtomicProgress.tsx +149 -0
  12. package/src/atoms/AtomicText.tsx +55 -0
  13. package/src/atoms/__tests__/AtomicButton.test.tsx +107 -0
  14. package/src/atoms/__tests__/AtomicIcon.test.tsx +110 -0
  15. package/src/atoms/__tests__/AtomicInput.test.tsx +195 -0
  16. package/src/atoms/datepicker/components/DatePickerButton.tsx +112 -0
  17. package/src/atoms/datepicker/components/DatePickerModal.tsx +143 -0
  18. package/src/atoms/fab/styles/fabStyles.ts +98 -0
  19. package/src/atoms/fab/types/index.ts +88 -0
  20. package/src/atoms/index.ts +70 -0
  21. package/src/atoms/input/hooks/useInputState.ts +63 -0
  22. package/src/atoms/input/styles/inputStylesHelper.ts +120 -0
  23. package/src/atoms/picker/components/PickerChips.tsx +57 -0
  24. package/src/atoms/picker/components/PickerModal.tsx +214 -0
  25. package/src/atoms/picker/styles/pickerStyles.ts +223 -0
  26. package/src/atoms/picker/types/index.ts +42 -0
  27. package/src/index.ts +133 -52
  28. package/src/molecules/ConfirmationModal.tsx +42 -0
  29. package/src/molecules/ConfirmationModalContent.tsx +87 -0
  30. package/src/molecules/ConfirmationModalMain.tsx +91 -0
  31. package/src/molecules/FormField.tsx +155 -0
  32. package/src/molecules/IconContainer.tsx +79 -0
  33. package/src/molecules/ListItem.tsx +35 -0
  34. package/src/molecules/ScreenHeader.tsx +171 -0
  35. package/src/molecules/SearchBar.tsx +198 -0
  36. package/src/molecules/confirmation-modal/components.tsx +94 -0
  37. package/src/molecules/confirmation-modal/index.ts +7 -0
  38. package/src/molecules/confirmation-modal/styles/confirmationModalStyles.ts +133 -0
  39. package/src/molecules/confirmation-modal/types/index.ts +41 -0
  40. package/src/molecules/confirmation-modal/useConfirmationModal.ts +50 -0
  41. package/src/molecules/index.ts +19 -0
  42. package/src/molecules/listitem/index.ts +6 -0
  43. package/src/molecules/listitem/styles/listItemStyles.ts +37 -0
  44. package/src/molecules/listitem/types/index.ts +21 -0
  45. package/src/organisms/AppHeader.tsx +136 -0
  46. package/src/organisms/FormContainer.tsx +169 -0
  47. package/src/organisms/ScreenLayout.tsx +183 -0
  48. package/src/organisms/index.ts +31 -0
  49. package/src/responsive/config.ts +139 -0
  50. package/src/responsive/deviceDetection.ts +155 -0
  51. package/src/responsive/gridUtils.ts +79 -0
  52. package/src/responsive/index.ts +52 -0
  53. package/src/responsive/platformConstants.ts +98 -0
  54. package/src/responsive/responsive.ts +61 -0
  55. package/src/responsive/responsiveLayout.ts +137 -0
  56. package/src/responsive/responsiveSizing.ts +134 -0
  57. package/src/responsive/useResponsive.ts +140 -0
  58. package/src/responsive/validation.ts +158 -0
  59. package/src/theme/core/BaseTokens.ts +42 -0
  60. package/src/theme/core/ColorPalette.ts +29 -0
  61. package/src/theme/core/CustomColors.ts +122 -0
  62. package/src/theme/core/NavigationTheme.ts +72 -0
  63. package/src/theme/core/TokenFactory.ts +103 -0
  64. package/src/theme/core/colors/ColorUtils.ts +53 -0
  65. package/src/theme/core/colors/DarkColors.ts +146 -0
  66. package/src/theme/core/colors/LightColors.ts +146 -0
  67. package/src/theme/core/constants/DesignConstants.ts +31 -0
  68. package/src/theme/core/themes.ts +118 -0
  69. package/src/theme/core/tokens/BaseTokens.ts +144 -0
  70. package/src/theme/core/tokens/Borders.ts +43 -0
  71. package/src/theme/core/tokens/Sizes.ts +51 -0
  72. package/src/theme/core/tokens/Spacing.ts +38 -0
  73. package/src/theme/core/tokens/Typography.ts +143 -0
  74. package/src/theme/hooks/useAppDesignTokens.ts +45 -0
  75. package/src/theme/hooks/useCommonStyles.ts +248 -0
  76. package/src/theme/hooks/useThemedStyles.ts +68 -0
  77. package/src/theme/index.ts +94 -0
  78. package/src/theme/infrastructure/globalThemeStore.ts +69 -0
  79. package/src/theme/infrastructure/storage/ThemeStorage.ts +93 -0
  80. package/src/theme/infrastructure/stores/themeStore.ts +109 -0
  81. package/src/typography/__tests__/colorValidationUtils.test.ts +180 -0
  82. package/src/typography/__tests__/textColorUtils.test.ts +185 -0
  83. package/src/typography/__tests__/textStyleUtils.test.ts +168 -0
  84. package/src/typography/domain/entities/TypographyTypes.ts +88 -0
  85. package/src/typography/index.ts +53 -0
  86. package/src/typography/presentation/utils/colorValidationUtils.ts +133 -0
  87. package/src/typography/presentation/utils/textColorUtils.ts +205 -0
  88. package/src/typography/presentation/utils/textStyleUtils.ts +159 -0
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Grid Utilities
3
+ * Responsive grid sizing and column calculations
4
+ */
5
+
6
+ import { getScreenDimensions } from './deviceDetection';
7
+ import { DEVICE_BREAKPOINTS, GRID_CONFIG } from './config';
8
+ import { validateNumber } from './validation';
9
+
10
+ /**
11
+ * Responsive grid columns
12
+ * Returns number of columns for grid layouts
13
+ *
14
+ * @param mobileColumns - Number of columns for mobile devices (default: 2)
15
+ * @param tabletColumns - Number of columns for tablet devices (default: 4)
16
+ * @returns Responsive number of grid columns
17
+ */
18
+ export const getResponsiveGridColumns = (
19
+ mobileColumns: number = GRID_CONFIG.DEFAULT_MOBILE_COLUMNS,
20
+ tabletColumns: number = GRID_CONFIG.DEFAULT_TABLET_COLUMNS
21
+ ): number => {
22
+ try {
23
+ const validatedMobile = validateNumber(mobileColumns, 'mobileColumns', 1, 20);
24
+ const validatedTablet = validateNumber(tabletColumns, 'tabletColumns', 1, 20);
25
+
26
+ const { width } = getScreenDimensions();
27
+ return width >= DEVICE_BREAKPOINTS.TABLET ? validatedTablet : validatedMobile;
28
+ } catch {
29
+ return 2;
30
+ }
31
+ };
32
+
33
+ export interface GridCellSizeConfig {
34
+ columns: number;
35
+ rows: number;
36
+ horizontalPadding?: number;
37
+ verticalPadding?: number;
38
+ gap?: number;
39
+ headerHeight?: number;
40
+ tabBarHeight?: number;
41
+ statusBarHeight?: number;
42
+ }
43
+
44
+ /**
45
+ * Responsive grid cell size
46
+ * Calculates optimal square cell size for a grid that fills available space
47
+ *
48
+ * @param config - Grid configuration
49
+ * @returns Responsive cell size (width = height for square cells)
50
+ */
51
+ export const getResponsiveGridCellSize = (config: GridCellSizeConfig): number => {
52
+ try {
53
+ const {
54
+ columns,
55
+ rows,
56
+ horizontalPadding = 32,
57
+ verticalPadding = 32,
58
+ gap = 8,
59
+ headerHeight = 120,
60
+ tabBarHeight = 80,
61
+ statusBarHeight = 50,
62
+ } = config;
63
+
64
+ const { width, height } = getScreenDimensions();
65
+
66
+ const totalHorizontalGap = gap * (columns - 1);
67
+ const availableWidth = width - horizontalPadding - totalHorizontalGap;
68
+ const maxCellWidth = availableWidth / columns;
69
+
70
+ const totalVerticalGap = gap * (rows - 1);
71
+ const usedHeight = headerHeight + tabBarHeight + statusBarHeight + verticalPadding;
72
+ const availableHeight = height - usedHeight - totalVerticalGap;
73
+ const maxCellHeight = availableHeight / rows;
74
+
75
+ return Math.floor(Math.min(maxCellWidth, maxCellHeight));
76
+ } catch {
77
+ return 60;
78
+ }
79
+ };
@@ -0,0 +1,52 @@
1
+ /**
2
+ * @umituz/react-native-design-system-responsive - Public API
3
+ *
4
+ * Responsive design utilities for React Native - Screen dimensions, device detection,
5
+ * and responsive sizing utilities following Material Design 3 and iOS HIG principles.
6
+ *
7
+ * Usage:
8
+ * ```typescript
9
+ * import { useResponsive, isTablet, getResponsiveLogoSize } from '@umituz/react-native-design-system-responsive';
10
+ * ```
11
+ */
12
+
13
+ // Hook exports
14
+ export { useResponsive } from './useResponsive';
15
+ export type { UseResponsiveReturn } from './useResponsive';
16
+
17
+ // Utility function exports
18
+ export {
19
+ getScreenDimensions,
20
+ isSmallPhone,
21
+ isTablet,
22
+ getResponsiveLogoSize,
23
+ getResponsiveInputHeight,
24
+ getResponsiveHorizontalPadding,
25
+ getResponsiveBottomPosition,
26
+ getResponsiveFABPosition,
27
+ getResponsiveModalMaxHeight,
28
+ getResponsiveMinModalHeight,
29
+ getResponsiveIconContainerSize,
30
+ getResponsiveGridColumns,
31
+ getResponsiveGridCellSize,
32
+ type GridCellSizeConfig,
33
+ getResponsiveMaxWidth,
34
+ getResponsiveFontSize,
35
+ isLandscape,
36
+ getDeviceType,
37
+ DeviceType,
38
+ } from './responsive';
39
+
40
+ // Device detection exports
41
+ export { getSpacingMultiplier } from './deviceDetection';
42
+
43
+
44
+
45
+ // Platform constants exports
46
+ export {
47
+ IOS_HIG,
48
+ PLATFORM_CONSTANTS,
49
+ isValidTouchTarget,
50
+ getMinTouchTarget,
51
+ } from './platformConstants';
52
+
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Platform-Specific Constants
3
+ *
4
+ * Design system constants that ensure compliance with platform guidelines.
5
+ * These values are based on official Human Interface Guidelines (HIG) from Apple and Material Design from Google.
6
+ */
7
+
8
+ /**
9
+ * iOS Human Interface Guidelines (HIG) Constants
10
+ *
11
+ * @see https://developer.apple.com/design/human-interface-guidelines/layout
12
+ */
13
+ export const IOS_HIG = {
14
+ /**
15
+ * Minimum Touch Target Size
16
+ *
17
+ * Apple requires a minimum tappable area of 44pt x 44pt for ALL interactive controls.
18
+ * This is enforced during App Store review.
19
+ *
20
+ * @critical Violating this can result in App Store rejection
21
+ */
22
+ MIN_TOUCH_TARGET: 44,
23
+
24
+ /**
25
+ * Recommended Minimum Touch Target Size
26
+ *
27
+ * For better accessibility and usability, Apple recommends 48pt x 48pt.
28
+ */
29
+ RECOMMENDED_TOUCH_TARGET: 48,
30
+
31
+ /**
32
+ * Minimum Text Size
33
+ *
34
+ * Minimum font size for body text to ensure readability.
35
+ */
36
+ MIN_TEXT_SIZE: 17,
37
+
38
+
39
+ } as const;
40
+
41
+
42
+
43
+ /**
44
+ * Universal Platform Constants
45
+ *
46
+ * These values work across both iOS and Android, taking the more restrictive requirement.
47
+ */
48
+ export const PLATFORM_CONSTANTS = {
49
+ /**
50
+ * Minimum Touch Target Size
51
+ *
52
+ * Uses iOS requirement (44pt) as it's more restrictive than Android (48dp).
53
+ * This ensures compliance on both platforms.
54
+ */
55
+ MIN_TOUCH_TARGET: Math.max(IOS_HIG.MIN_TOUCH_TARGET, 48),
56
+
57
+ /**
58
+ * Recommended Touch Target Size
59
+ *
60
+ * Uses the higher value between iOS and Android recommendations.
61
+ */
62
+ RECOMMENDED_TOUCH_TARGET: 48,
63
+
64
+ /**
65
+ * Minimum Text Size
66
+ *
67
+ * Uses iOS requirement as it's larger.
68
+ */
69
+ MIN_TEXT_SIZE: Math.max(IOS_HIG.MIN_TEXT_SIZE, 14),
70
+ } as const;
71
+
72
+ /**
73
+ * Helper function to validate touch target size
74
+ *
75
+ * @param size - The size to validate (in pt/dp)
76
+ * @returns true if size meets platform requirements
77
+ */
78
+ export const isValidTouchTarget = (size: number): boolean => {
79
+ return size >= IOS_HIG.MIN_TOUCH_TARGET;
80
+ };
81
+
82
+ /**
83
+ * Helper function to get minimum touch target for component
84
+ *
85
+ * @param componentType - The type of component ('button' | 'input' | 'icon' | 'generic')
86
+ * @returns The minimum touch target size for that component type
87
+ */
88
+ export const getMinTouchTarget = (componentType: 'button' | 'input' | 'icon' | 'generic' = 'generic'): number => {
89
+ switch (componentType) {
90
+ case 'button':
91
+ case 'input':
92
+ return PLATFORM_CONSTANTS.RECOMMENDED_TOUCH_TARGET; // 48pt recommended for buttons and inputs
93
+ case 'icon':
94
+ case 'generic':
95
+ default:
96
+ return IOS_HIG.MIN_TOUCH_TARGET; // 44pt minimum for icons and generic elements
97
+ }
98
+ };
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Responsive Design Utilities
3
+ *
4
+ * Centralized responsive sizing and spacing utilities to prevent
5
+ * Apple App Store rejection due to layout issues on different devices.
6
+ *
7
+ * This is the main export file that imports from specialized modules.
8
+ */
9
+
10
+ // Device detection
11
+ export {
12
+ getScreenDimensions,
13
+ isSmallPhone,
14
+ isTablet,
15
+ isLandscape,
16
+ getDeviceType,
17
+ DeviceType,
18
+ } from './deviceDetection';
19
+
20
+ // Responsive sizing
21
+ export {
22
+ getResponsiveLogoSize,
23
+ getResponsiveInputHeight,
24
+ getResponsiveIconContainerSize,
25
+ getResponsiveMaxWidth,
26
+ getResponsiveFontSize,
27
+ getResponsiveGridColumns,
28
+ getResponsiveGridCellSize,
29
+ type GridCellSizeConfig,
30
+ } from './responsiveSizing';
31
+
32
+ // Responsive layout
33
+ export {
34
+ getResponsiveHorizontalPadding,
35
+ getResponsiveBottomPosition,
36
+ getResponsiveFABPosition,
37
+ getResponsiveModalMaxHeight,
38
+ getResponsiveMinModalHeight,
39
+ } from './responsiveLayout';
40
+
41
+ // Platform constants
42
+ export {
43
+ IOS_HIG,
44
+ PLATFORM_CONSTANTS,
45
+ isValidTouchTarget,
46
+ getMinTouchTarget,
47
+ } from './platformConstants';
48
+
49
+ // Configuration
50
+ export {
51
+ DEVICE_BREAKPOINTS,
52
+ RESPONSIVE_PERCENTAGES,
53
+ SIZE_CONSTRAINTS,
54
+ LAYOUT_CONSTANTS,
55
+ HEIGHT_THRESHOLDS,
56
+ GRID_CONFIG,
57
+ VALIDATION_CONSTRAINTS,
58
+ } from './config';
59
+
60
+
61
+
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Responsive Layout Utilities
3
+ * Layout utilities for positioning and spacing.
4
+ */
5
+
6
+ import { getScreenDimensions } from './deviceDetection';
7
+ import {
8
+ DEVICE_BREAKPOINTS,
9
+ LAYOUT_CONSTANTS,
10
+ HEIGHT_THRESHOLDS,
11
+ SIZE_CONSTRAINTS,
12
+ } from './config';
13
+ import { validateNumber, validateSafeAreaInsets } from './validation';
14
+
15
+ /**
16
+ * Responsive horizontal padding
17
+ * @param basePadding - Base padding value (default: 16)
18
+ * @param insets - Safe area insets object
19
+ */
20
+ export const getResponsiveHorizontalPadding = (
21
+ basePadding: number = LAYOUT_CONSTANTS.HORIZONTAL_PADDING_BASE,
22
+ insets: { left?: number; right?: number } = { left: 0, right: 0 }
23
+ ): number => {
24
+ try {
25
+ const validatedBasePadding = validateNumber(basePadding, 'basePadding', 0, 100);
26
+ validateSafeAreaInsets(insets);
27
+
28
+ const { width } = getScreenDimensions();
29
+ const { left = 0, right = 0 } = insets;
30
+
31
+ if (width >= DEVICE_BREAKPOINTS.TABLET) {
32
+ const tabletPadding = validatedBasePadding * 1.5;
33
+ return Math.max(
34
+ tabletPadding,
35
+ left + LAYOUT_CONSTANTS.HORIZONTAL_PADDING_BASE,
36
+ right + LAYOUT_CONSTANTS.HORIZONTAL_PADDING_BASE
37
+ );
38
+ }
39
+
40
+ return Math.max(
41
+ validatedBasePadding,
42
+ left + LAYOUT_CONSTANTS.SAFE_AREA_OFFSET,
43
+ right + LAYOUT_CONSTANTS.SAFE_AREA_OFFSET
44
+ );
45
+ } catch {
46
+ return 16;
47
+ }
48
+ };
49
+
50
+ /**
51
+ * Responsive bottom positioning
52
+ * @param basePosition - Base bottom position (default: 32)
53
+ * @param insets - Safe area insets object
54
+ */
55
+ export const getResponsiveBottomPosition = (
56
+ basePosition: number = LAYOUT_CONSTANTS.BOTTOM_POSITION_BASE,
57
+ insets: { bottom?: number } = { bottom: 0 }
58
+ ): number => {
59
+ try {
60
+ const validatedBasePosition = validateNumber(basePosition, 'basePosition', 0, 500);
61
+ validateSafeAreaInsets(insets);
62
+
63
+ const { bottom = 0 } = insets;
64
+ return Math.max(validatedBasePosition, bottom + LAYOUT_CONSTANTS.SAFE_AREA_OFFSET);
65
+ } catch {
66
+ return 32;
67
+ }
68
+ };
69
+
70
+ /**
71
+ * Responsive FAB position
72
+ * @param insets - Safe area insets object
73
+ */
74
+ export const getResponsiveFABPosition = (
75
+ insets: { bottom?: number; right?: number } = { bottom: 0, right: 0 }
76
+ ): { bottom: number; right: number } => {
77
+ try {
78
+ validateSafeAreaInsets(insets);
79
+ const { width } = getScreenDimensions();
80
+ const { bottom = 0, right = 0 } = insets;
81
+
82
+ if (width >= DEVICE_BREAKPOINTS.TABLET) {
83
+ return {
84
+ bottom: Math.max(LAYOUT_CONSTANTS.FAB_BOTTOM_TABLET, bottom + LAYOUT_CONSTANTS.TAB_BAR_OFFSET),
85
+ right: Math.max(LAYOUT_CONSTANTS.FAB_RIGHT_TABLET, right + LAYOUT_CONSTANTS.HORIZONTAL_PADDING_BASE),
86
+ };
87
+ }
88
+
89
+ return {
90
+ bottom: Math.max(LAYOUT_CONSTANTS.TAB_BAR_OFFSET, bottom + LAYOUT_CONSTANTS.SAFE_AREA_OFFSET),
91
+ right: Math.max(LAYOUT_CONSTANTS.FAB_RIGHT_PHONE, right + LAYOUT_CONSTANTS.SAFE_AREA_OFFSET),
92
+ };
93
+ } catch {
94
+ return { bottom: 90, right: 20 };
95
+ }
96
+ };
97
+
98
+ /**
99
+ * Responsive modal max height
100
+ */
101
+ export const getResponsiveModalMaxHeight = (): string => {
102
+ try {
103
+ const { height } = getScreenDimensions();
104
+
105
+ if (height <= HEIGHT_THRESHOLDS.SMALL_DEVICE) {
106
+ return LAYOUT_CONSTANTS.MODAL_HEIGHT_SMALL;
107
+ } else if (height >= HEIGHT_THRESHOLDS.LARGE_DEVICE) {
108
+ return LAYOUT_CONSTANTS.MODAL_HEIGHT_TABLET;
109
+ }
110
+
111
+ return LAYOUT_CONSTANTS.MODAL_HEIGHT_STANDARD;
112
+ } catch {
113
+ return '70%';
114
+ }
115
+ };
116
+
117
+ /**
118
+ * Responsive modal min height
119
+ */
120
+ export const getResponsiveMinModalHeight = (): number => {
121
+ try {
122
+ const { height } = getScreenDimensions();
123
+
124
+ if (height <= HEIGHT_THRESHOLDS.SMALL_DEVICE) {
125
+ const calculatedHeight = height * 0.4;
126
+ return Math.max(calculatedHeight, SIZE_CONSTRAINTS.MODAL_MIN_SMALL);
127
+ } else if (height >= HEIGHT_THRESHOLDS.LARGE_DEVICE) {
128
+ const calculatedHeight = height * 0.35;
129
+ return Math.min(Math.max(calculatedHeight, SIZE_CONSTRAINTS.MODAL_MIN_TABLET), SIZE_CONSTRAINTS.MODAL_MAX_TABLET);
130
+ }
131
+
132
+ const calculatedHeight = height * 0.45;
133
+ return Math.max(calculatedHeight, SIZE_CONSTRAINTS.MODAL_MIN_STANDARD);
134
+ } catch {
135
+ return 300;
136
+ }
137
+ };
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Responsive Sizing Utilities
3
+ * Responsive sizing utilities for UI components.
4
+ */
5
+
6
+ import { getScreenDimensions } from './deviceDetection';
7
+ import {
8
+ DEVICE_BREAKPOINTS,
9
+ RESPONSIVE_PERCENTAGES,
10
+ SIZE_CONSTRAINTS,
11
+ HEIGHT_THRESHOLDS,
12
+ } from './config';
13
+ import { validateNumber, validateFontSize, safePercentage, clamp } from './validation';
14
+
15
+ // Re-export grid utilities
16
+ export {
17
+ getResponsiveGridColumns,
18
+ getResponsiveGridCellSize,
19
+ type GridCellSizeConfig,
20
+ } from './gridUtils';
21
+
22
+ /**
23
+ * Responsive logo/icon size
24
+ * @param baseSize - Base logo size (default: 140)
25
+ */
26
+ export const getResponsiveLogoSize = (baseSize: number = 140): number => {
27
+ try {
28
+ const validatedBaseSize = validateNumber(baseSize, 'baseSize', 50, 500);
29
+ const { width } = getScreenDimensions();
30
+
31
+ if (width <= DEVICE_BREAKPOINTS.SMALL_PHONE) {
32
+ const calculatedSize = safePercentage(width, RESPONSIVE_PERCENTAGES.LOGO_SMALL_PHONE_MAX);
33
+ return clamp(calculatedSize, SIZE_CONSTRAINTS.LOGO_MIN_SMALL, SIZE_CONSTRAINTS.LOGO_MAX_SMALL);
34
+ } else if (width >= DEVICE_BREAKPOINTS.TABLET) {
35
+ const calculatedSize = safePercentage(width, RESPONSIVE_PERCENTAGES.LOGO_TABLET_MAX);
36
+ return clamp(calculatedSize, SIZE_CONSTRAINTS.LOGO_MIN_TABLET, SIZE_CONSTRAINTS.LOGO_MAX_TABLET);
37
+ }
38
+
39
+ return validatedBaseSize;
40
+ } catch {
41
+ return 140;
42
+ }
43
+ };
44
+
45
+ /**
46
+ * Responsive multiline input height
47
+ * @param baseHeight - Base input height (default: 200)
48
+ */
49
+ export const getResponsiveInputHeight = (baseHeight: number = 200): number => {
50
+ try {
51
+ const validatedBaseHeight = validateNumber(baseHeight, 'baseHeight', 50, 500);
52
+ const { height } = getScreenDimensions();
53
+
54
+ if (height <= HEIGHT_THRESHOLDS.SMALL_DEVICE) {
55
+ const calculatedHeight = safePercentage(height, RESPONSIVE_PERCENTAGES.INPUT_SMALL_DEVICE);
56
+ return Math.min(calculatedHeight, SIZE_CONSTRAINTS.INPUT_MAX_SMALL);
57
+ } else if (height <= HEIGHT_THRESHOLDS.MEDIUM_DEVICE) {
58
+ const calculatedHeight = safePercentage(height, RESPONSIVE_PERCENTAGES.INPUT_MEDIUM_DEVICE);
59
+ return Math.min(calculatedHeight, SIZE_CONSTRAINTS.INPUT_MAX_MEDIUM);
60
+ }
61
+
62
+ return Math.min(validatedBaseHeight, SIZE_CONSTRAINTS.INPUT_MAX_LARGE);
63
+ } catch {
64
+ return 200;
65
+ }
66
+ };
67
+
68
+ /**
69
+ * Responsive icon container size
70
+ * @param baseSize - Base container size (default: 140)
71
+ */
72
+ export const getResponsiveIconContainerSize = (baseSize: number = 140): number => {
73
+ try {
74
+ const validatedBaseSize = validateNumber(baseSize, 'baseSize', 50, 300);
75
+ const { width } = getScreenDimensions();
76
+
77
+ if (width <= DEVICE_BREAKPOINTS.SMALL_PHONE) {
78
+ const calculatedSize = safePercentage(width, RESPONSIVE_PERCENTAGES.ICON_CONTAINER_SMALL_PHONE);
79
+ return Math.min(calculatedSize, SIZE_CONSTRAINTS.ICON_MAX_SMALL);
80
+ } else if (width >= DEVICE_BREAKPOINTS.TABLET) {
81
+ const calculatedSize = safePercentage(width, RESPONSIVE_PERCENTAGES.ICON_CONTAINER_TABLET);
82
+ return Math.min(calculatedSize, SIZE_CONSTRAINTS.ICON_MAX_TABLET);
83
+ }
84
+
85
+ return validatedBaseSize;
86
+ } catch {
87
+ return 140;
88
+ }
89
+ };
90
+
91
+ /**
92
+ * Responsive max width for content
93
+ * @param baseWidth - Base content width (default: 400)
94
+ */
95
+ export const getResponsiveMaxWidth = (baseWidth: number = 400): number => {
96
+ try {
97
+ const validatedBaseWidth = validateNumber(baseWidth, 'baseWidth', 100, 1000);
98
+ const { width } = getScreenDimensions();
99
+
100
+ if (width <= DEVICE_BREAKPOINTS.SMALL_PHONE) {
101
+ return safePercentage(width, RESPONSIVE_PERCENTAGES.CONTENT_SMALL_PHONE);
102
+ } else if (width >= DEVICE_BREAKPOINTS.TABLET) {
103
+ const calculatedWidth = safePercentage(width, RESPONSIVE_PERCENTAGES.CONTENT_TABLET);
104
+ return Math.min(calculatedWidth, SIZE_CONSTRAINTS.CONTENT_MAX_TABLET);
105
+ }
106
+
107
+ const maxWidth = safePercentage(width, RESPONSIVE_PERCENTAGES.CONTENT_PHONE);
108
+ return Math.min(maxWidth, validatedBaseWidth);
109
+ } catch {
110
+ return 400;
111
+ }
112
+ };
113
+
114
+ /**
115
+ * Responsive font size
116
+ * @param baseFontSize - Base font size
117
+ */
118
+ export const getResponsiveFontSize = (baseFontSize: number): number => {
119
+ try {
120
+ const validatedBaseSize = validateFontSize(baseFontSize);
121
+ const { width } = getScreenDimensions();
122
+
123
+ if (width <= DEVICE_BREAKPOINTS.SMALL_PHONE) {
124
+ const scaledSize = validatedBaseSize * RESPONSIVE_PERCENTAGES.FONT_SMALL_PHONE;
125
+ return Math.max(scaledSize, SIZE_CONSTRAINTS.FONT_MIN_SIZE);
126
+ } else if (width >= DEVICE_BREAKPOINTS.TABLET) {
127
+ return validatedBaseSize * RESPONSIVE_PERCENTAGES.FONT_TABLET;
128
+ }
129
+
130
+ return validatedBaseSize;
131
+ } catch {
132
+ return 16;
133
+ }
134
+ };
@@ -0,0 +1,140 @@
1
+ /**
2
+ * useResponsive Hook
3
+ *
4
+ * React Hook for accessing responsive utilities with real-time dimension updates
5
+ * and safe area insets integration.
6
+ *
7
+ * Usage:
8
+ * ```tsx
9
+ * const { logoSize, inputHeight, fabPosition, isSmallDevice } = useResponsive();
10
+ * ```
11
+ */
12
+
13
+ import { useCallback, useMemo } from 'react';
14
+ import { useWindowDimensions } from 'react-native';
15
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
16
+ import {
17
+ getResponsiveLogoSize,
18
+ getResponsiveInputHeight,
19
+ getResponsiveHorizontalPadding,
20
+ getResponsiveBottomPosition,
21
+ getResponsiveFABPosition,
22
+ getResponsiveModalMaxHeight,
23
+ getResponsiveMinModalHeight,
24
+ getResponsiveIconContainerSize,
25
+ getResponsiveGridColumns,
26
+ getResponsiveMaxWidth,
27
+ getResponsiveFontSize,
28
+ isSmallPhone,
29
+ isTablet,
30
+ isLandscape,
31
+ getDeviceType,
32
+ getMinTouchTarget,
33
+ DeviceType,
34
+ } from './responsive';
35
+ import { getSpacingMultiplier } from './deviceDetection';
36
+
37
+ export interface UseResponsiveReturn {
38
+ // Device info
39
+ width: number;
40
+ height: number;
41
+ isSmallDevice: boolean;
42
+ isTabletDevice: boolean;
43
+ isLandscapeDevice: boolean;
44
+ deviceType: DeviceType;
45
+
46
+ // Safe area insets
47
+ insets: {
48
+ top: number;
49
+ bottom: number;
50
+ left: number;
51
+ right: number;
52
+ };
53
+
54
+ // Responsive sizes
55
+ logoSize: number;
56
+ inputHeight: number;
57
+ iconContainerSize: number;
58
+ maxContentWidth: number;
59
+ minTouchTarget: number;
60
+
61
+ // Responsive positioning
62
+ horizontalPadding: number;
63
+ bottomPosition: number;
64
+ fabPosition: { bottom: number; right: number };
65
+
66
+ // Responsive layout
67
+ modalMaxHeight: string;
68
+ modalMinHeight: number;
69
+ gridColumns: number;
70
+ spacingMultiplier: number;
71
+
72
+ // Utility functions
73
+ getLogoSize: (baseSize?: number) => number;
74
+ getInputHeight: (baseHeight?: number) => number;
75
+ getIconSize: (baseSize?: number) => number;
76
+ getMaxWidth: (baseWidth?: number) => number;
77
+ getFontSize: (baseFontSize: number) => number;
78
+ getGridCols: (mobile?: number, tablet?: number) => number;
79
+ }
80
+
81
+ /**
82
+ * Hook for responsive design utilities
83
+ * Automatically updates when screen dimensions or orientation changes
84
+ */
85
+ export const useResponsive = (): UseResponsiveReturn => {
86
+ const { width, height } = useWindowDimensions();
87
+ const insets = useSafeAreaInsets();
88
+
89
+ // Memoize utility functions to prevent unnecessary re-renders
90
+ const getLogoSize = useCallback((baseSize?: number) => getResponsiveLogoSize(baseSize), []);
91
+ const getInputHeight = useCallback((baseHeight?: number) => getResponsiveInputHeight(baseHeight), []);
92
+ const getIconSize = useCallback((baseSize?: number) => getResponsiveIconContainerSize(baseSize), []);
93
+ const getMaxWidth = useCallback((baseWidth?: number) => getResponsiveMaxWidth(baseWidth), []);
94
+ const getFontSize = useCallback((baseFontSize: number) => getResponsiveFontSize(baseFontSize), []);
95
+ const getGridCols = useCallback((mobile?: number, tablet?: number) => getResponsiveGridColumns(mobile, tablet), []);
96
+
97
+ // Memoize responsive values to prevent unnecessary recalculations
98
+ const responsiveValues = useMemo(() => ({
99
+ // Device info
100
+ width,
101
+ height,
102
+ isSmallDevice: isSmallPhone(),
103
+ isTabletDevice: isTablet(),
104
+ isLandscapeDevice: isLandscape(),
105
+ deviceType: getDeviceType(),
106
+
107
+ // Safe area insets
108
+ insets,
109
+
110
+ // Responsive sizes (with default values)
111
+ logoSize: getResponsiveLogoSize(),
112
+ inputHeight: getResponsiveInputHeight(),
113
+ iconContainerSize: getResponsiveIconContainerSize(),
114
+ maxContentWidth: getResponsiveMaxWidth(),
115
+ minTouchTarget: getMinTouchTarget(),
116
+
117
+ // Responsive positioning
118
+ horizontalPadding: getResponsiveHorizontalPadding(undefined, insets),
119
+ bottomPosition: getResponsiveBottomPosition(undefined, insets),
120
+ fabPosition: getResponsiveFABPosition(insets),
121
+
122
+ // Responsive layout
123
+ modalMaxHeight: getResponsiveModalMaxHeight(),
124
+ modalMinHeight: getResponsiveMinModalHeight(),
125
+ gridColumns: getResponsiveGridColumns(),
126
+ spacingMultiplier: getSpacingMultiplier(),
127
+
128
+ // Utility functions (memoized)
129
+ getLogoSize,
130
+ getInputHeight,
131
+ getIconSize,
132
+ getMaxWidth,
133
+ getFontSize,
134
+ getGridCols,
135
+ }), [width, height, insets]);
136
+
137
+ return responsiveValues;
138
+ };
139
+
140
+