@umituz/react-native-design-system 1.14.0 → 2.0.0

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 (87) 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/AtomicChip.tsx +226 -0
  5. package/src/atoms/AtomicDatePicker.tsx +255 -0
  6. package/src/atoms/AtomicFab.tsx +99 -0
  7. package/src/atoms/AtomicIcon.tsx +149 -0
  8. package/src/atoms/AtomicInput.tsx +308 -0
  9. package/src/atoms/AtomicPicker.tsx +310 -0
  10. package/src/atoms/AtomicProgress.tsx +149 -0
  11. package/src/atoms/AtomicText.tsx +55 -0
  12. package/src/atoms/__tests__/AtomicButton.test.tsx +107 -0
  13. package/src/atoms/__tests__/AtomicIcon.test.tsx +110 -0
  14. package/src/atoms/__tests__/AtomicInput.test.tsx +195 -0
  15. package/src/atoms/datepicker/components/DatePickerButton.tsx +112 -0
  16. package/src/atoms/datepicker/components/DatePickerModal.tsx +143 -0
  17. package/src/atoms/fab/styles/fabStyles.ts +98 -0
  18. package/src/atoms/fab/types/index.ts +88 -0
  19. package/src/atoms/index.ts +70 -0
  20. package/src/atoms/input/hooks/useInputState.ts +63 -0
  21. package/src/atoms/input/styles/inputStylesHelper.ts +120 -0
  22. package/src/atoms/picker/components/PickerChips.tsx +57 -0
  23. package/src/atoms/picker/components/PickerModal.tsx +214 -0
  24. package/src/atoms/picker/styles/pickerStyles.ts +223 -0
  25. package/src/atoms/picker/types/index.ts +42 -0
  26. package/src/index.ts +148 -79
  27. package/src/molecules/ConfirmationModal.tsx +42 -0
  28. package/src/molecules/ConfirmationModalContent.tsx +87 -0
  29. package/src/molecules/ConfirmationModalMain.tsx +91 -0
  30. package/src/molecules/FormField.tsx +155 -0
  31. package/src/molecules/IconContainer.tsx +79 -0
  32. package/src/molecules/ListItem.tsx +35 -0
  33. package/src/molecules/ScreenHeader.tsx +171 -0
  34. package/src/molecules/SearchBar.tsx +198 -0
  35. package/src/molecules/confirmation-modal/components.tsx +94 -0
  36. package/src/molecules/confirmation-modal/index.ts +7 -0
  37. package/src/molecules/confirmation-modal/styles/confirmationModalStyles.ts +133 -0
  38. package/src/molecules/confirmation-modal/types/index.ts +41 -0
  39. package/src/molecules/confirmation-modal/useConfirmationModal.ts +50 -0
  40. package/src/molecules/index.ts +19 -0
  41. package/src/molecules/listitem/index.ts +6 -0
  42. package/src/molecules/listitem/styles/listItemStyles.ts +37 -0
  43. package/src/molecules/listitem/types/index.ts +21 -0
  44. package/src/organisms/AppHeader.tsx +136 -0
  45. package/src/organisms/FormContainer.tsx +169 -0
  46. package/src/organisms/ScreenLayout.tsx +183 -0
  47. package/src/organisms/index.ts +31 -0
  48. package/src/responsive/config.ts +139 -0
  49. package/src/responsive/deviceDetection.ts +155 -0
  50. package/src/responsive/gridUtils.ts +79 -0
  51. package/src/responsive/index.ts +52 -0
  52. package/src/responsive/platformConstants.ts +98 -0
  53. package/src/responsive/responsive.ts +61 -0
  54. package/src/responsive/responsiveLayout.ts +137 -0
  55. package/src/responsive/responsiveSizing.ts +134 -0
  56. package/src/responsive/useResponsive.ts +140 -0
  57. package/src/responsive/validation.ts +158 -0
  58. package/src/theme/core/BaseTokens.ts +42 -0
  59. package/src/theme/core/ColorPalette.ts +29 -0
  60. package/src/theme/core/CustomColors.ts +122 -0
  61. package/src/theme/core/NavigationTheme.ts +72 -0
  62. package/src/theme/core/TokenFactory.ts +103 -0
  63. package/src/theme/core/colors/ColorUtils.ts +53 -0
  64. package/src/theme/core/colors/DarkColors.ts +146 -0
  65. package/src/theme/core/colors/LightColors.ts +146 -0
  66. package/src/theme/core/constants/DesignConstants.ts +31 -0
  67. package/src/theme/core/themes.ts +118 -0
  68. package/src/theme/core/tokens/BaseTokens.ts +144 -0
  69. package/src/theme/core/tokens/Borders.ts +43 -0
  70. package/src/theme/core/tokens/Sizes.ts +51 -0
  71. package/src/theme/core/tokens/Spacing.ts +38 -0
  72. package/src/theme/core/tokens/Typography.ts +143 -0
  73. package/src/theme/hooks/useAppDesignTokens.ts +45 -0
  74. package/src/theme/hooks/useCommonStyles.ts +248 -0
  75. package/src/theme/hooks/useThemedStyles.ts +68 -0
  76. package/src/theme/index.ts +94 -0
  77. package/src/theme/infrastructure/globalThemeStore.ts +69 -0
  78. package/src/theme/infrastructure/storage/ThemeStorage.ts +93 -0
  79. package/src/theme/infrastructure/stores/themeStore.ts +109 -0
  80. package/src/typography/__tests__/colorValidationUtils.test.ts +180 -0
  81. package/src/typography/__tests__/textColorUtils.test.ts +185 -0
  82. package/src/typography/__tests__/textStyleUtils.test.ts +168 -0
  83. package/src/typography/domain/entities/TypographyTypes.ts +88 -0
  84. package/src/typography/index.ts +53 -0
  85. package/src/typography/presentation/utils/colorValidationUtils.ts +133 -0
  86. package/src/typography/presentation/utils/textColorUtils.ts +205 -0
  87. package/src/typography/presentation/utils/textStyleUtils.ts +159 -0
@@ -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
+
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Input Validation Utilities
3
+ *
4
+ * Centralized validation for all responsive utility functions.
5
+ * Ensures type safety and prevents runtime errors.
6
+ */
7
+
8
+ import { VALIDATION_CONSTRAINTS } from './config';
9
+
10
+ /**
11
+ * Custom error class for responsive utilities
12
+ */
13
+ export class ResponsiveValidationError extends Error {
14
+ constructor(message: string) {
15
+ super(`[Responsive] ${message}`);
16
+ this.name = 'ResponsiveValidationError';
17
+ }
18
+ }
19
+
20
+ /**
21
+ * Validates a numeric input parameter
22
+ * @param value - The value to validate
23
+ * @param paramName - Parameter name for error messages
24
+ * @param min - Minimum allowed value
25
+ * @param max - Maximum allowed value
26
+ * @throws ResponsiveValidationError if validation fails
27
+ */
28
+ export const validateNumber = (
29
+ value: number | undefined,
30
+ paramName: string,
31
+ min: number = VALIDATION_CONSTRAINTS.MIN_BASE_SIZE,
32
+ max: number = VALIDATION_CONSTRAINTS.MAX_BASE_SIZE
33
+ ): number => {
34
+ if (value === undefined || value === null) {
35
+ throw new ResponsiveValidationError(`${paramName} is required`);
36
+ }
37
+
38
+ if (typeof value !== 'number' || isNaN(value)) {
39
+ throw new ResponsiveValidationError(`${paramName} must be a valid number`);
40
+ }
41
+
42
+ if (!isFinite(value)) {
43
+ throw new ResponsiveValidationError(`${paramName} must be a finite number`);
44
+ }
45
+
46
+ if (value < min) {
47
+ throw new ResponsiveValidationError(`${paramName} must be at least ${min}`);
48
+ }
49
+
50
+ if (value > max) {
51
+ throw new ResponsiveValidationError(`${paramName} must be at most ${max}`);
52
+ }
53
+
54
+ return value;
55
+ };
56
+
57
+ /**
58
+ * Validates font size input
59
+ * @param fontSize - Font size to validate
60
+ * @param paramName - Parameter name for error messages
61
+ * @returns Validated font size
62
+ */
63
+ export const validateFontSize = (fontSize: number, paramName: string = 'fontSize'): number => {
64
+ return validateNumber(
65
+ fontSize,
66
+ paramName,
67
+ VALIDATION_CONSTRAINTS.MIN_BASE_FONT_SIZE,
68
+ VALIDATION_CONSTRAINTS.MAX_BASE_FONT_SIZE
69
+ );
70
+ };
71
+
72
+ /**
73
+ * Validates screen dimensions
74
+ * @param width - Screen width
75
+ * @param height - Screen height
76
+ * @throws ResponsiveValidationError if validation fails
77
+ */
78
+ export const validateScreenDimensions = (width: number, height: number): void => {
79
+ validateNumber(
80
+ width,
81
+ 'width',
82
+ VALIDATION_CONSTRAINTS.MIN_SCREEN_DIMENSION,
83
+ VALIDATION_CONSTRAINTS.MAX_SCREEN_DIMENSION
84
+ );
85
+
86
+ validateNumber(
87
+ height,
88
+ 'height',
89
+ VALIDATION_CONSTRAINTS.MIN_SCREEN_DIMENSION,
90
+ VALIDATION_CONSTRAINTS.MAX_SCREEN_DIMENSION
91
+ );
92
+ };
93
+
94
+ /**
95
+ * Validates safe area insets
96
+ * @param insets - Safe area insets object
97
+ * @throws ResponsiveValidationError if validation fails
98
+ */
99
+ export const validateSafeAreaInsets = (insets: {
100
+ top?: number;
101
+ bottom?: number;
102
+ left?: number;
103
+ right?: number;
104
+ }): void => {
105
+ if (!insets || typeof insets !== 'object') {
106
+ throw new ResponsiveValidationError('Safe area insets must be an object');
107
+ }
108
+
109
+ const { top = 0, bottom = 0, left = 0, right = 0 } = insets;
110
+
111
+ validateNumber(top, 'insets.top', 0, 1000);
112
+ validateNumber(bottom, 'insets.bottom', 0, 1000);
113
+ validateNumber(left, 'insets.left', 0, 1000);
114
+ validateNumber(right, 'insets.right', 0, 1000);
115
+ };
116
+
117
+ /**
118
+ * Validates grid column parameters
119
+ * @param mobileColumns - Number of columns for mobile
120
+ * @param tabletColumns - Number of columns for tablet
121
+ * @throws ResponsiveValidationError if validation fails
122
+ */
123
+ export const validateGridColumns = (
124
+ mobileColumns?: number,
125
+ tabletColumns?: number
126
+ ): void => {
127
+ if (mobileColumns !== undefined) {
128
+ validateNumber(mobileColumns, 'mobileColumns', 1, 20);
129
+ }
130
+
131
+ if (tabletColumns !== undefined) {
132
+ validateNumber(tabletColumns, 'tabletColumns', 1, 20);
133
+ }
134
+ };
135
+
136
+ /**
137
+ * Clamps a value between min and max bounds
138
+ * @param value - Value to clamp
139
+ * @param min - Minimum value
140
+ * @param max - Maximum value
141
+ * @returns Clamped value
142
+ */
143
+ export const clamp = (value: number, min: number, max: number): number => {
144
+ return Math.min(Math.max(value, min), max);
145
+ };
146
+
147
+ /**
148
+ * Safely calculates a percentage of a value
149
+ * @param value - Base value
150
+ * @param percentage - Percentage (0-1)
151
+ * @returns Calculated percentage
152
+ */
153
+ export const safePercentage = (value: number, percentage: number): number => {
154
+ const validatedValue = validateNumber(value, 'value', 0, Infinity);
155
+ const validatedPercentage = clamp(percentage, 0, 1);
156
+
157
+ return validatedValue * validatedPercentage;
158
+ };
@@ -0,0 +1,42 @@
1
+ /**
2
+ * BASE TOKENS - Main Export
3
+ *
4
+ * Aggregates all token modules into BASE_TOKENS
5
+ */
6
+
7
+ import { spacing } from './tokens/Spacing';
8
+ import { typography } from './tokens/Typography';
9
+ import { borders } from './tokens/Borders';
10
+ import { iconSizes, opacity, avatarSizes, sizes } from './tokens/Sizes';
11
+ import type { BaseTokens } from './tokens/BaseTokens';
12
+
13
+ /**
14
+ * BASE_TOKENS - Static design tokens
15
+ * These values don't change with theme (light/dark)
16
+ */
17
+ export const BASE_TOKENS: BaseTokens = {
18
+ spacing,
19
+ typography,
20
+ borders,
21
+ iconSizes,
22
+ opacity,
23
+ avatarSizes,
24
+ sizes,
25
+ };
26
+
27
+ // Convenience exports
28
+ export { spacing, typography, borders, iconSizes, opacity, avatarSizes, sizes };
29
+
30
+
31
+
32
+ // Type exports
33
+ export type {
34
+ BaseTokens,
35
+ Spacing,
36
+ Typography,
37
+ Borders,
38
+ IconSizes,
39
+ Opacity,
40
+ AvatarSizes,
41
+ ComponentSizes
42
+ } from './tokens/BaseTokens';
@@ -0,0 +1,29 @@
1
+ /**
2
+ * COLOR PALETTE - Main Export
3
+ *
4
+ * Aggregates all color modules and provides theme utilities
5
+ */
6
+
7
+ import { lightColors } from './colors/LightColors';
8
+ import { darkColors } from './colors/DarkColors';
9
+ import { withAlpha, isValidHexColor } from './colors/ColorUtils';
10
+
11
+ export type ColorPalette = typeof lightColors;
12
+ export type ThemeMode = 'light' | 'dark';
13
+
14
+ /**
15
+ * Get color palette for specific theme mode
16
+ * @param mode - 'light' or 'dark'
17
+ * @returns Color palette object
18
+ */
19
+ export const getColorPalette = (mode: ThemeMode): ColorPalette => {
20
+ return mode === 'dark' ? darkColors : lightColors;
21
+ };
22
+
23
+ // Export all colors and utilities
24
+ export {
25
+ lightColors,
26
+ darkColors,
27
+ withAlpha,
28
+ isValidHexColor
29
+ };
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Custom Colors Types
3
+ *
4
+ * Types for custom theme color overrides
5
+ */
6
+
7
+ import type { ColorPalette } from './ColorPalette';
8
+ import { isValidHexColor } from './colors/ColorUtils';
9
+
10
+ /**
11
+ * Custom theme colors that can override default colors
12
+ */
13
+ export interface CustomThemeColors {
14
+ primary?: string;
15
+ primaryLight?: string;
16
+ primaryDark?: string;
17
+ secondary?: string;
18
+ secondaryLight?: string;
19
+ secondaryDark?: string;
20
+ accent?: string;
21
+ accentLight?: string;
22
+ accentDark?: string;
23
+ buttonPrimary?: string;
24
+ buttonSecondary?: string;
25
+ }
26
+
27
+ /**
28
+ * Validate custom colors object
29
+ * @param customColors - Custom colors to validate
30
+ * @returns true if all colors are valid hex format
31
+ */
32
+ export const validateCustomColors = (customColors: CustomThemeColors): boolean => {
33
+ const colorValues = Object.values(customColors).filter(Boolean) as string[];
34
+
35
+ for (const color of colorValues) {
36
+ if (!isValidHexColor(color)) {
37
+ if (__DEV__) {
38
+ console.warn('[validateCustomColors] Invalid hex color:', color);
39
+ }
40
+ return false;
41
+ }
42
+ }
43
+
44
+ return true;
45
+ };
46
+
47
+ /**
48
+ * Apply custom colors to color palette
49
+ * @param palette - Base color palette
50
+ * @param customColors - Custom colors to apply
51
+ * @returns Color palette with custom colors applied
52
+ */
53
+ export const applyCustomColors = (
54
+ palette: ColorPalette,
55
+ customColors?: CustomThemeColors,
56
+ ): ColorPalette => {
57
+ if (!customColors) {
58
+ return palette;
59
+ }
60
+
61
+ // Validate custom colors
62
+ if (!validateCustomColors(customColors)) {
63
+ if (__DEV__) {
64
+ console.error('[applyCustomColors] Invalid custom colors provided, using defaults');
65
+ }
66
+ return palette;
67
+ }
68
+
69
+ const result: Partial<ColorPalette> = {
70
+ ...palette,
71
+ };
72
+
73
+ // Apply custom primary colors
74
+ if (customColors.primary) {
75
+ result.primary = customColors.primary;
76
+ if (!customColors.buttonPrimary) {
77
+ result.buttonPrimary = customColors.primary;
78
+ }
79
+ }
80
+ if (customColors.primaryLight) {
81
+ result.primaryLight = customColors.primaryLight;
82
+ }
83
+ if (customColors.primaryDark) {
84
+ result.primaryDark = customColors.primaryDark;
85
+ }
86
+
87
+ // Apply custom secondary colors
88
+ if (customColors.secondary) {
89
+ result.secondary = customColors.secondary;
90
+ if (!customColors.buttonSecondary) {
91
+ result.buttonSecondary = customColors.secondary;
92
+ }
93
+ }
94
+ if (customColors.secondaryLight) {
95
+ result.secondaryLight = customColors.secondaryLight;
96
+ }
97
+ if (customColors.secondaryDark) {
98
+ result.secondaryDark = customColors.secondaryDark;
99
+ }
100
+
101
+ // Apply custom accent colors
102
+ if (customColors.accent) {
103
+ result.accent = customColors.accent;
104
+ }
105
+ if (customColors.accentLight) {
106
+ result.accentLight = customColors.accentLight;
107
+ }
108
+ if (customColors.accentDark) {
109
+ result.accentDark = customColors.accentDark;
110
+ }
111
+
112
+ // Apply custom button colors (override primary/secondary if set)
113
+ if (customColors.buttonPrimary) {
114
+ result.buttonPrimary = customColors.buttonPrimary;
115
+ }
116
+ if (customColors.buttonSecondary) {
117
+ result.buttonSecondary = customColors.buttonSecondary;
118
+ }
119
+
120
+ return result as ColorPalette;
121
+ };
122
+
@@ -0,0 +1,72 @@
1
+ import type { ColorPalette, ThemeMode } from "./ColorPalette";
2
+ import type { ExtendedColorPalette } from "./themes";
3
+
4
+ /**
5
+ * Font weight type compatible with React Navigation v7
6
+ */
7
+ type FontWeight =
8
+ | "normal"
9
+ | "bold"
10
+ | "100"
11
+ | "200"
12
+ | "300"
13
+ | "400"
14
+ | "500"
15
+ | "600"
16
+ | "700"
17
+ | "800"
18
+ | "900";
19
+
20
+ /**
21
+ * Font configuration for navigation theme
22
+ */
23
+ interface FontConfig {
24
+ fontFamily: string;
25
+ fontWeight: FontWeight;
26
+ }
27
+
28
+ /**
29
+ * Navigation theme type compatible with React Navigation v7
30
+ */
31
+ export interface NavigationTheme {
32
+ dark: boolean;
33
+ colors: {
34
+ primary: string;
35
+ background: string;
36
+ card: string;
37
+ text: string;
38
+ border: string;
39
+ notification: string;
40
+ };
41
+ fonts: {
42
+ regular: FontConfig;
43
+ medium: FontConfig;
44
+ bold: FontConfig;
45
+ heavy: FontConfig;
46
+ };
47
+ }
48
+
49
+ /**
50
+ * Creates a React Navigation theme from design system colors
51
+ * Compatible with React Navigation v7+
52
+ */
53
+ export const createNavigationTheme = (
54
+ colors: ColorPalette | ExtendedColorPalette,
55
+ themeMode: ThemeMode
56
+ ): NavigationTheme => ({
57
+ dark: themeMode === "dark",
58
+ colors: {
59
+ primary: colors.primary,
60
+ background: colors.backgroundPrimary,
61
+ card: colors.surface,
62
+ text: colors.textPrimary,
63
+ border: colors.borderLight,
64
+ notification: colors.error,
65
+ },
66
+ fonts: {
67
+ regular: { fontFamily: "System", fontWeight: "400" },
68
+ medium: { fontFamily: "System", fontWeight: "500" },
69
+ bold: { fontFamily: "System", fontWeight: "700" },
70
+ heavy: { fontFamily: "System", fontWeight: "800" },
71
+ },
72
+ });