@umituz/react-native-design-system 1.5.33 → 1.5.34
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 +4 -1
- package/src/presentation/atoms/AtomicDatePicker.tsx +2 -1
- package/src/presentation/atoms/AtomicInput.tsx +19 -12
- package/src/index.js +0 -100
- package/src/presentation/atoms/AtomicAvatar.js +0 -84
- package/src/presentation/atoms/AtomicAvatarGroup.js +0 -82
- package/src/presentation/atoms/AtomicBadge.js +0 -167
- package/src/presentation/atoms/AtomicButton.js +0 -171
- package/src/presentation/atoms/AtomicCard.js +0 -69
- package/src/presentation/atoms/AtomicChip.js +0 -130
- package/src/presentation/atoms/AtomicDatePicker.js +0 -245
- package/src/presentation/atoms/AtomicDivider.js +0 -57
- package/src/presentation/atoms/AtomicFab.js +0 -67
- package/src/presentation/atoms/AtomicFilter.js +0 -103
- package/src/presentation/atoms/AtomicFormError.js +0 -63
- package/src/presentation/atoms/AtomicIcon.js +0 -29
- package/src/presentation/atoms/AtomicImage.js +0 -91
- package/src/presentation/atoms/AtomicInput.js +0 -201
- package/src/presentation/atoms/AtomicNumberInput.js +0 -124
- package/src/presentation/atoms/AtomicPicker.js +0 -298
- package/src/presentation/atoms/AtomicProgress.js +0 -79
- package/src/presentation/atoms/AtomicSearchBar.js +0 -45
- package/src/presentation/atoms/AtomicSort.js +0 -76
- package/src/presentation/atoms/AtomicSwitch.js +0 -103
- package/src/presentation/atoms/AtomicText.js +0 -22
- package/src/presentation/atoms/AtomicTextArea.js +0 -195
- package/src/presentation/atoms/AtomicTouchable.js +0 -137
- package/src/presentation/atoms/fab/styles/fabStyles.js +0 -62
- package/src/presentation/atoms/fab/types/index.js +0 -1
- package/src/presentation/atoms/filter/styles/filterStyles.js +0 -28
- package/src/presentation/atoms/filter/types/index.js +0 -1
- package/src/presentation/atoms/index.js +0 -145
- package/src/presentation/atoms/input/hooks/useInputState.js +0 -12
- package/src/presentation/atoms/input/styles/inputStyles.js +0 -58
- package/src/presentation/atoms/input/types/index.js +0 -1
- package/src/presentation/atoms/picker/styles/pickerStyles.js +0 -176
- package/src/presentation/atoms/picker/types/index.js +0 -1
- package/src/presentation/atoms/touchable/styles/touchableStyles.js +0 -53
- package/src/presentation/atoms/touchable/types/index.js +0 -1
- package/src/presentation/hooks/useResponsive.js +0 -81
- package/src/presentation/molecules/AtomicConfirmationModal.js +0 -153
- package/src/presentation/molecules/EmptyState.js +0 -67
- package/src/presentation/molecules/FormField.js +0 -75
- package/src/presentation/molecules/GridContainer.js +0 -76
- package/src/presentation/molecules/IconContainer.js +0 -59
- package/src/presentation/molecules/ListItem.js +0 -23
- package/src/presentation/molecules/ScreenHeader.js +0 -93
- package/src/presentation/molecules/SearchBar.js +0 -46
- package/src/presentation/molecules/SectionCard.js +0 -46
- package/src/presentation/molecules/SectionContainer.js +0 -63
- package/src/presentation/molecules/SectionHeader.js +0 -72
- package/src/presentation/molecules/confirmation-modal/styles/confirmationModalStyles.js +0 -114
- package/src/presentation/molecules/confirmation-modal/types/index.js +0 -6
- package/src/presentation/molecules/index.js +0 -16
- package/src/presentation/molecules/listitem/styles/listItemStyles.js +0 -14
- package/src/presentation/molecules/listitem/types/index.js +0 -1
- package/src/presentation/organisms/AppHeader.js +0 -77
- package/src/presentation/organisms/FormContainer.js +0 -126
- package/src/presentation/organisms/ScreenLayout.js +0 -68
- package/src/presentation/organisms/index.js +0 -13
- package/src/presentation/tokens/commonStyles.js +0 -219
- package/src/presentation/utils/platformConstants.js +0 -113
- package/src/presentation/utils/responsive.js +0 -451
- package/src/presentation/utils/variants/compound.js +0 -15
- package/src/presentation/utils/variants/core.js +0 -22
- package/src/presentation/utils/variants/helpers.js +0 -9
- package/src/presentation/utils/variants.js +0 -3
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
import { IOS_HIG, getMinTouchTarget } from '../../../utils/platformConstants';
|
|
2
|
-
/**
|
|
3
|
-
* Picker container styles with iOS HIG compliance
|
|
4
|
-
*
|
|
5
|
-
* All picker sizes meet Apple's minimum touch target requirement of 44pt.
|
|
6
|
-
* @see https://developer.apple.com/design/human-interface-guidelines/layout
|
|
7
|
-
*/
|
|
8
|
-
export const getPickerContainerStyles = (tokens) => ({
|
|
9
|
-
base: {
|
|
10
|
-
flexDirection: 'row',
|
|
11
|
-
alignItems: 'center',
|
|
12
|
-
justifyContent: 'space-between',
|
|
13
|
-
borderWidth: tokens.borders.width.thin,
|
|
14
|
-
borderColor: tokens.colors.border,
|
|
15
|
-
backgroundColor: tokens.colors.surface,
|
|
16
|
-
},
|
|
17
|
-
size: {
|
|
18
|
-
// ✅ iOS HIG Compliant: All sizes >= 44pt minimum touch target
|
|
19
|
-
sm: {
|
|
20
|
-
height: IOS_HIG.MIN_TOUCH_TARGET, // 44pt - iOS minimum
|
|
21
|
-
paddingHorizontal: tokens.spacing.sm,
|
|
22
|
-
borderRadius: tokens.borders.radius.sm,
|
|
23
|
-
},
|
|
24
|
-
md: {
|
|
25
|
-
height: getMinTouchTarget('input'), // 48pt - Recommended
|
|
26
|
-
paddingHorizontal: tokens.spacing.md,
|
|
27
|
-
borderRadius: tokens.borders.radius.md,
|
|
28
|
-
},
|
|
29
|
-
lg: {
|
|
30
|
-
height: 56, // 56pt - Large touch target
|
|
31
|
-
paddingHorizontal: tokens.spacing.lg,
|
|
32
|
-
borderRadius: tokens.borders.radius.md,
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
state: {
|
|
36
|
-
error: {
|
|
37
|
-
borderColor: tokens.colors.error,
|
|
38
|
-
borderWidth: tokens.borders.width.medium,
|
|
39
|
-
},
|
|
40
|
-
disabled: {
|
|
41
|
-
backgroundColor: tokens.colors.surfaceDisabled,
|
|
42
|
-
opacity: 0.6,
|
|
43
|
-
},
|
|
44
|
-
},
|
|
45
|
-
});
|
|
46
|
-
export const getPickerLabelStyles = (tokens) => ({
|
|
47
|
-
base: {
|
|
48
|
-
marginBottom: tokens.spacing.xs,
|
|
49
|
-
color: tokens.colors.textPrimary,
|
|
50
|
-
fontWeight: '600',
|
|
51
|
-
},
|
|
52
|
-
size: {
|
|
53
|
-
sm: tokens.typography.bodySmall,
|
|
54
|
-
md: tokens.typography.bodyMedium,
|
|
55
|
-
lg: tokens.typography.bodyLarge,
|
|
56
|
-
},
|
|
57
|
-
});
|
|
58
|
-
export const getPickerPlaceholderStyles = (tokens) => ({
|
|
59
|
-
base: {
|
|
60
|
-
color: tokens.colors.textSecondary,
|
|
61
|
-
},
|
|
62
|
-
size: {
|
|
63
|
-
sm: tokens.typography.bodySmall,
|
|
64
|
-
md: tokens.typography.bodyMedium,
|
|
65
|
-
lg: tokens.typography.bodyLarge,
|
|
66
|
-
},
|
|
67
|
-
});
|
|
68
|
-
export const getPickerValueStyles = (tokens) => ({
|
|
69
|
-
base: {
|
|
70
|
-
flex: 1,
|
|
71
|
-
color: tokens.colors.textPrimary,
|
|
72
|
-
},
|
|
73
|
-
size: {
|
|
74
|
-
sm: tokens.typography.bodySmall,
|
|
75
|
-
md: tokens.typography.bodyMedium,
|
|
76
|
-
lg: tokens.typography.bodyLarge,
|
|
77
|
-
},
|
|
78
|
-
});
|
|
79
|
-
export const getPickerErrorStyles = (tokens) => ({
|
|
80
|
-
marginTop: tokens.spacing.xs,
|
|
81
|
-
color: tokens.colors.error,
|
|
82
|
-
...tokens.typography.bodySmall,
|
|
83
|
-
});
|
|
84
|
-
export const getModalOverlayStyles = (tokens) => ({
|
|
85
|
-
flex: 1,
|
|
86
|
-
backgroundColor: tokens.colors.overlayMedium,
|
|
87
|
-
justifyContent: 'flex-end',
|
|
88
|
-
});
|
|
89
|
-
export const getModalContainerStyles = (tokens, maxHeight) => ({
|
|
90
|
-
backgroundColor: tokens.colors.backgroundPrimary,
|
|
91
|
-
borderTopLeftRadius: tokens.borders.radius.lg,
|
|
92
|
-
borderTopRightRadius: tokens.borders.radius.lg,
|
|
93
|
-
maxHeight: maxHeight,
|
|
94
|
-
paddingBottom: tokens.spacing.lg,
|
|
95
|
-
});
|
|
96
|
-
export const getModalHeaderStyles = (tokens) => ({
|
|
97
|
-
flexDirection: 'row',
|
|
98
|
-
alignItems: 'center',
|
|
99
|
-
justifyContent: 'space-between',
|
|
100
|
-
paddingHorizontal: tokens.spacing.lg,
|
|
101
|
-
paddingVertical: tokens.spacing.md,
|
|
102
|
-
borderBottomWidth: tokens.borders.width.thin,
|
|
103
|
-
borderBottomColor: tokens.colors.border,
|
|
104
|
-
});
|
|
105
|
-
export const getModalTitleStyles = (tokens) => ({
|
|
106
|
-
...tokens.typography.titleLarge,
|
|
107
|
-
color: tokens.colors.textPrimary,
|
|
108
|
-
fontWeight: '600',
|
|
109
|
-
});
|
|
110
|
-
export const getSearchContainerStyles = (tokens) => ({
|
|
111
|
-
flexDirection: 'row',
|
|
112
|
-
alignItems: 'center',
|
|
113
|
-
backgroundColor: tokens.colors.surfaceVariant,
|
|
114
|
-
paddingHorizontal: tokens.spacing.md,
|
|
115
|
-
paddingVertical: tokens.spacing.sm,
|
|
116
|
-
marginHorizontal: tokens.spacing.lg,
|
|
117
|
-
marginTop: tokens.spacing.md,
|
|
118
|
-
borderRadius: tokens.borders.radius.md,
|
|
119
|
-
});
|
|
120
|
-
export const getSearchInputStyles = (tokens) => ({
|
|
121
|
-
flex: 1,
|
|
122
|
-
marginLeft: tokens.spacing.sm,
|
|
123
|
-
...tokens.typography.bodyMedium,
|
|
124
|
-
color: tokens.colors.textPrimary,
|
|
125
|
-
});
|
|
126
|
-
export const getOptionContainerStyles = (tokens, isSelected, isDisabled) => ({
|
|
127
|
-
flexDirection: 'row',
|
|
128
|
-
alignItems: 'center',
|
|
129
|
-
paddingHorizontal: tokens.spacing.lg,
|
|
130
|
-
paddingVertical: tokens.spacing.md,
|
|
131
|
-
backgroundColor: isSelected ? tokens.colors.primaryContainer : 'transparent',
|
|
132
|
-
opacity: isDisabled ? 0.5 : 1,
|
|
133
|
-
});
|
|
134
|
-
export const getOptionTextStyles = (tokens, isSelected) => ({
|
|
135
|
-
flex: 1,
|
|
136
|
-
marginLeft: tokens.spacing.sm,
|
|
137
|
-
...tokens.typography.bodyLarge,
|
|
138
|
-
color: isSelected ? tokens.colors.primary : tokens.colors.textPrimary,
|
|
139
|
-
fontWeight: isSelected ? '600' : '400',
|
|
140
|
-
});
|
|
141
|
-
export const getOptionDescriptionStyles = (tokens) => ({
|
|
142
|
-
...tokens.typography.bodySmall,
|
|
143
|
-
color: tokens.colors.textSecondary,
|
|
144
|
-
marginLeft: tokens.spacing.sm,
|
|
145
|
-
marginTop: tokens.spacing.xs,
|
|
146
|
-
});
|
|
147
|
-
export const getEmptyStateStyles = (tokens) => ({
|
|
148
|
-
alignItems: 'center',
|
|
149
|
-
justifyContent: 'center',
|
|
150
|
-
paddingVertical: tokens.spacing.xxl,
|
|
151
|
-
});
|
|
152
|
-
export const getEmptyStateTextStyles = (tokens) => ({
|
|
153
|
-
...tokens.typography.bodyLarge,
|
|
154
|
-
color: tokens.colors.textSecondary,
|
|
155
|
-
textAlign: 'center',
|
|
156
|
-
marginTop: tokens.spacing.md,
|
|
157
|
-
});
|
|
158
|
-
export const getChipContainerStyles = (tokens) => ({
|
|
159
|
-
flexDirection: 'row',
|
|
160
|
-
flexWrap: 'wrap',
|
|
161
|
-
gap: tokens.spacing.xs,
|
|
162
|
-
marginTop: tokens.spacing.xs,
|
|
163
|
-
});
|
|
164
|
-
export const getChipStyles = (tokens) => ({
|
|
165
|
-
flexDirection: 'row',
|
|
166
|
-
alignItems: 'center',
|
|
167
|
-
backgroundColor: tokens.colors.primaryContainer,
|
|
168
|
-
paddingHorizontal: tokens.spacing.sm,
|
|
169
|
-
paddingVertical: tokens.spacing.xs,
|
|
170
|
-
borderRadius: tokens.borders.radius.sm,
|
|
171
|
-
});
|
|
172
|
-
export const getChipTextStyles = (tokens) => ({
|
|
173
|
-
...tokens.typography.bodySmall,
|
|
174
|
-
color: tokens.colors.primary,
|
|
175
|
-
marginRight: tokens.spacing.xs,
|
|
176
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Get opacity value based on feedback strength
|
|
3
|
-
*/
|
|
4
|
-
export const getOpacityValue = (strength) => {
|
|
5
|
-
switch (strength) {
|
|
6
|
-
case 'subtle':
|
|
7
|
-
return 0.8;
|
|
8
|
-
case 'normal':
|
|
9
|
-
return 0.6;
|
|
10
|
-
case 'strong':
|
|
11
|
-
return 0.4;
|
|
12
|
-
default:
|
|
13
|
-
return 0.6;
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
|
-
/**
|
|
17
|
-
* Get base touchable container style
|
|
18
|
-
* Ensures minimum touch target size (iOS HIG: 48x48)
|
|
19
|
-
*/
|
|
20
|
-
export const getTouchableContainerStyle = () => ({
|
|
21
|
-
minWidth: 48,
|
|
22
|
-
minHeight: 48,
|
|
23
|
-
justifyContent: 'center',
|
|
24
|
-
alignItems: 'center',
|
|
25
|
-
});
|
|
26
|
-
/**
|
|
27
|
-
* Get disabled touchable style
|
|
28
|
-
*/
|
|
29
|
-
export const getDisabledStyle = () => ({
|
|
30
|
-
opacity: 0.5,
|
|
31
|
-
});
|
|
32
|
-
/**
|
|
33
|
-
* Convert number to HitSlop object
|
|
34
|
-
* If hitSlop is a number, apply it to all sides
|
|
35
|
-
*/
|
|
36
|
-
export const normalizeHitSlop = (hitSlop) => {
|
|
37
|
-
if (hitSlop === undefined)
|
|
38
|
-
return undefined;
|
|
39
|
-
if (typeof hitSlop === 'number') {
|
|
40
|
-
return {
|
|
41
|
-
top: hitSlop,
|
|
42
|
-
bottom: hitSlop,
|
|
43
|
-
left: hitSlop,
|
|
44
|
-
right: hitSlop,
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
return {
|
|
48
|
-
top: hitSlop.top || 0,
|
|
49
|
-
bottom: hitSlop.bottom || 0,
|
|
50
|
-
left: hitSlop.left || 0,
|
|
51
|
-
right: hitSlop.right || 0,
|
|
52
|
-
};
|
|
53
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,81 +0,0 @@
|
|
|
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
|
-
import { useWindowDimensions } from 'react-native';
|
|
13
|
-
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
14
|
-
import { getResponsiveLogoSize, getResponsiveInputHeight, getResponsiveHorizontalPadding, getResponsiveBottomPosition, getResponsiveFABPosition, getResponsiveModalMaxHeight, getResponsiveMinModalHeight, getResponsiveIconContainerSize, getResponsiveGridColumns, getResponsiveMaxWidth, getResponsiveFontSize, isSmallPhone, isTablet, isLandscape, getDeviceType, getMinTouchTargetSize, getSpacingMultiplier, getOnboardingIconMarginTop, getOnboardingIconMarginBottom, getOnboardingTitleMarginBottom, getOnboardingTextPadding, getOnboardingDescriptionMarginTop, getOnboardingIconSize, getFormBottomPadding, getInputIconSize, getFormContentWidth, getFormElementSpacing, } from '../utils/responsive';
|
|
15
|
-
/**
|
|
16
|
-
* Hook for responsive design utilities
|
|
17
|
-
* Automatically updates when screen dimensions or orientation changes
|
|
18
|
-
*/
|
|
19
|
-
export const useResponsive = () => {
|
|
20
|
-
const { width, height } = useWindowDimensions();
|
|
21
|
-
const insets = useSafeAreaInsets();
|
|
22
|
-
return {
|
|
23
|
-
// Device info
|
|
24
|
-
width,
|
|
25
|
-
height,
|
|
26
|
-
isSmallDevice: isSmallPhone(),
|
|
27
|
-
isTabletDevice: isTablet(),
|
|
28
|
-
isLandscapeMode: isLandscape(),
|
|
29
|
-
deviceType: getDeviceType(),
|
|
30
|
-
// Safe area insets
|
|
31
|
-
insets,
|
|
32
|
-
// Responsive sizes (with default values)
|
|
33
|
-
logoSize: getResponsiveLogoSize(),
|
|
34
|
-
inputHeight: getResponsiveInputHeight(),
|
|
35
|
-
iconContainerSize: getResponsiveIconContainerSize(),
|
|
36
|
-
maxContentWidth: getResponsiveMaxWidth(),
|
|
37
|
-
minTouchTarget: getMinTouchTargetSize(),
|
|
38
|
-
// Responsive positioning
|
|
39
|
-
horizontalPadding: getResponsiveHorizontalPadding(16, insets),
|
|
40
|
-
bottomPosition: getResponsiveBottomPosition(32, insets),
|
|
41
|
-
fabPosition: getResponsiveFABPosition(insets),
|
|
42
|
-
// Responsive layout
|
|
43
|
-
modalMaxHeight: getResponsiveModalMaxHeight(),
|
|
44
|
-
modalMinHeight: getResponsiveMinModalHeight(),
|
|
45
|
-
gridColumns: getResponsiveGridColumns(),
|
|
46
|
-
spacingMultiplier: getSpacingMultiplier(),
|
|
47
|
-
// Onboarding-specific spacing (pre-calculated, no component calculations)
|
|
48
|
-
onboardingIconMarginTop: getOnboardingIconMarginTop(),
|
|
49
|
-
onboardingIconMarginBottom: getOnboardingIconMarginBottom(),
|
|
50
|
-
onboardingIconSize: getOnboardingIconSize(),
|
|
51
|
-
onboardingTitleMarginBottom: getOnboardingTitleMarginBottom(),
|
|
52
|
-
onboardingTextPadding: getOnboardingTextPadding(),
|
|
53
|
-
onboardingDescriptionMarginTop: getOnboardingDescriptionMarginTop(),
|
|
54
|
-
// Form-specific spacing (pre-calculated, universal)
|
|
55
|
-
formBottomPadding: getFormBottomPadding(insets.bottom),
|
|
56
|
-
inputIconSize: getInputIconSize(),
|
|
57
|
-
formContentWidth: getFormContentWidth(),
|
|
58
|
-
formElementSpacing: getFormElementSpacing(),
|
|
59
|
-
// Utility functions (allow custom base values)
|
|
60
|
-
getLogoSize: (baseSize) => getResponsiveLogoSize(baseSize),
|
|
61
|
-
getInputHeight: (baseHeight) => getResponsiveInputHeight(baseHeight),
|
|
62
|
-
getIconSize: (baseSize) => getResponsiveIconContainerSize(baseSize),
|
|
63
|
-
getMaxWidth: (baseWidth) => getResponsiveMaxWidth(baseWidth),
|
|
64
|
-
getFontSize: (baseFontSize) => getResponsiveFontSize(baseFontSize),
|
|
65
|
-
getGridCols: (mobile, tablet) => getResponsiveGridColumns(mobile, tablet),
|
|
66
|
-
};
|
|
67
|
-
};
|
|
68
|
-
/**
|
|
69
|
-
* Shorthand hook for just responsive sizes
|
|
70
|
-
*/
|
|
71
|
-
export const useResponsiveSizes = () => {
|
|
72
|
-
const { logoSize, inputHeight, iconContainerSize, maxContentWidth } = useResponsive();
|
|
73
|
-
return { logoSize, inputHeight, iconContainerSize, maxContentWidth };
|
|
74
|
-
};
|
|
75
|
-
/**
|
|
76
|
-
* Shorthand hook for just device type checks
|
|
77
|
-
*/
|
|
78
|
-
export const useDeviceType = () => {
|
|
79
|
-
const { isSmallDevice, isTabletDevice, deviceType } = useResponsive();
|
|
80
|
-
return { isSmallDevice, isTabletDevice, deviceType };
|
|
81
|
-
};
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AtomicConfirmationModal - Universal Confirmation Dialog
|
|
3
|
-
*
|
|
4
|
-
* A reusable confirmation modal for destructive and important actions.
|
|
5
|
-
* Follows Material Design 3 dialog patterns and accessibility guidelines.
|
|
6
|
-
*
|
|
7
|
-
* Features:
|
|
8
|
-
* - Multiple variants (default, destructive, warning, success)
|
|
9
|
-
* - Configurable text and icons
|
|
10
|
-
* - Backdrop dismissal
|
|
11
|
-
* - Full keyboard and screen reader support
|
|
12
|
-
* - Theme-aware styling
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```tsx
|
|
16
|
-
* // Destructive confirmation (delete)
|
|
17
|
-
* <AtomicConfirmationModal
|
|
18
|
-
* visible={showDeleteModal}
|
|
19
|
-
* variant="destructive"
|
|
20
|
-
* title="Delete Item?"
|
|
21
|
-
* message="This action cannot be undone. All data will be permanently deleted."
|
|
22
|
-
* confirmText="Delete"
|
|
23
|
-
* cancelText="Cancel"
|
|
24
|
-
* onConfirm={handleDelete}
|
|
25
|
-
* onCancel={() => setShowDeleteModal(false)}
|
|
26
|
-
* />
|
|
27
|
-
*
|
|
28
|
-
* // Generic confirmation
|
|
29
|
-
* <AtomicConfirmationModal
|
|
30
|
-
* visible={showConfirmModal}
|
|
31
|
-
* variant="default"
|
|
32
|
-
* title="Confirm Action"
|
|
33
|
-
* message="Are you sure you want to proceed?"
|
|
34
|
-
* onConfirm={handleConfirm}
|
|
35
|
-
* onCancel={() => setShowConfirmModal(false)}
|
|
36
|
-
* />
|
|
37
|
-
* ```
|
|
38
|
-
*/
|
|
39
|
-
import React from 'react';
|
|
40
|
-
import { View, Modal, TouchableOpacity } from 'react-native';
|
|
41
|
-
import { useAppDesignTokens } from '@umituz/react-native-theme';
|
|
42
|
-
import { AtomicText } from '../atoms/AtomicText';
|
|
43
|
-
import { AtomicButton } from '../atoms/AtomicButton';
|
|
44
|
-
import { AtomicIcon } from '../atoms/AtomicIcon';
|
|
45
|
-
import { getVariantConfig, getModalOverlayStyle, getBackdropStyle, getModalContainerStyle, getIconContainerStyle, getTitleContainerStyle, getMessageContainerStyle, getButtonContainerStyle, getButtonStyle, } from './confirmation-modal/styles/confirmationModalStyles';
|
|
46
|
-
export const AtomicConfirmationModal = ({ visible, title, message, variant = 'default', confirmText, cancelText, icon, onConfirm, onCancel, showBackdrop = true, backdropDismissible = true, style, testID = 'atomic-confirmation-modal', }) => {
|
|
47
|
-
const tokens = useAppDesignTokens();
|
|
48
|
-
// Get variant-specific configuration (icon and color only)
|
|
49
|
-
const variantConfig = getVariantConfig(variant, tokens);
|
|
50
|
-
// Determine final icon
|
|
51
|
-
const finalIcon = icon || variantConfig.icon;
|
|
52
|
-
// Handle backdrop press
|
|
53
|
-
const handleBackdropPress = () => {
|
|
54
|
-
if (backdropDismissible) {
|
|
55
|
-
onCancel();
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
return (<Modal visible={visible} transparent animationType="fade" onRequestClose={onCancel} statusBarTranslucent testID={testID}>
|
|
59
|
-
<View style={getModalOverlayStyle(tokens)}>
|
|
60
|
-
{/* Backdrop - Tap to dismiss if enabled */}
|
|
61
|
-
{showBackdrop && (<TouchableOpacity style={getBackdropStyle()} activeOpacity={1} onPress={handleBackdropPress} testID={`${testID}-backdrop`}/>)}
|
|
62
|
-
|
|
63
|
-
{/* Modal Container */}
|
|
64
|
-
<View style={[getModalContainerStyle(tokens), style]}>
|
|
65
|
-
{/* Icon */}
|
|
66
|
-
<View style={getIconContainerStyle(tokens)}>
|
|
67
|
-
<AtomicIcon name={finalIcon} size="xl" color={variantConfig.iconColor} testID={`${testID}-icon`}/>
|
|
68
|
-
</View>
|
|
69
|
-
|
|
70
|
-
{/* Title */}
|
|
71
|
-
<View style={getTitleContainerStyle(tokens)}>
|
|
72
|
-
<AtomicText type="titleLarge" style={{
|
|
73
|
-
color: tokens.colors.textPrimary,
|
|
74
|
-
textAlign: 'center',
|
|
75
|
-
fontWeight: tokens.typography.bold,
|
|
76
|
-
}} testID={`${testID}-title`}>
|
|
77
|
-
{title}
|
|
78
|
-
</AtomicText>
|
|
79
|
-
</View>
|
|
80
|
-
|
|
81
|
-
{/* Message */}
|
|
82
|
-
<View style={getMessageContainerStyle(tokens)}>
|
|
83
|
-
<AtomicText type="bodyMedium" style={{
|
|
84
|
-
color: tokens.colors.textSecondary,
|
|
85
|
-
textAlign: 'center',
|
|
86
|
-
lineHeight: tokens.typography.bodyMedium.lineHeight,
|
|
87
|
-
}} testID={`${testID}-message`}>
|
|
88
|
-
{message}
|
|
89
|
-
</AtomicText>
|
|
90
|
-
</View>
|
|
91
|
-
|
|
92
|
-
{/* Action Buttons */}
|
|
93
|
-
<View style={getButtonContainerStyle(tokens)}>
|
|
94
|
-
{/* Cancel Button */}
|
|
95
|
-
<AtomicButton variant="outline" size="md" onPress={onCancel} style={getButtonStyle()} testID={`${testID}-cancel-button`}>
|
|
96
|
-
{cancelText}
|
|
97
|
-
</AtomicButton>
|
|
98
|
-
|
|
99
|
-
{/* Confirm Button */}
|
|
100
|
-
<AtomicButton variant={variant === 'destructive' ? 'primary' : 'primary'} size="md" onPress={onConfirm} style={[
|
|
101
|
-
getButtonStyle(),
|
|
102
|
-
...(variant === 'destructive' ? [{ backgroundColor: tokens.colors.error }] : []),
|
|
103
|
-
...(variant === 'warning' ? [{ backgroundColor: tokens.colors.warning }] : []),
|
|
104
|
-
...(variant === 'success' ? [{ backgroundColor: tokens.colors.success }] : []),
|
|
105
|
-
]} testID={`${testID}-confirm-button`}>
|
|
106
|
-
{confirmText}
|
|
107
|
-
</AtomicButton>
|
|
108
|
-
</View>
|
|
109
|
-
</View>
|
|
110
|
-
</View>
|
|
111
|
-
</Modal>);
|
|
112
|
-
};
|
|
113
|
-
/**
|
|
114
|
-
* Hook for managing confirmation modal state
|
|
115
|
-
*
|
|
116
|
-
* @example
|
|
117
|
-
* ```tsx
|
|
118
|
-
* const { showConfirmation, confirmationProps } = useConfirmationModal({
|
|
119
|
-
* title: 'Delete Item?',
|
|
120
|
-
* message: 'This cannot be undone',
|
|
121
|
-
* variant: 'destructive',
|
|
122
|
-
* onConfirm: handleDelete,
|
|
123
|
-
* });
|
|
124
|
-
*
|
|
125
|
-
* // In JSX
|
|
126
|
-
* <AtomicConfirmationModal {...confirmationProps} />
|
|
127
|
-
* <Button onPress={showConfirmation}>Delete</Button>
|
|
128
|
-
* ```
|
|
129
|
-
*/
|
|
130
|
-
export const useConfirmationModal = (config) => {
|
|
131
|
-
const [visible, setVisible] = React.useState(false);
|
|
132
|
-
const showConfirmation = () => setVisible(true);
|
|
133
|
-
const hideConfirmation = () => setVisible(false);
|
|
134
|
-
const handleConfirm = () => {
|
|
135
|
-
config.onConfirm();
|
|
136
|
-
hideConfirmation();
|
|
137
|
-
};
|
|
138
|
-
const confirmationProps = {
|
|
139
|
-
visible,
|
|
140
|
-
title: config.title,
|
|
141
|
-
message: config.message,
|
|
142
|
-
variant: config.variant || 'default',
|
|
143
|
-
confirmText: config.confirmText,
|
|
144
|
-
cancelText: config.cancelText,
|
|
145
|
-
onConfirm: handleConfirm,
|
|
146
|
-
onCancel: hideConfirmation,
|
|
147
|
-
};
|
|
148
|
-
return {
|
|
149
|
-
showConfirmation,
|
|
150
|
-
hideConfirmation,
|
|
151
|
-
confirmationProps,
|
|
152
|
-
};
|
|
153
|
-
};
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* EmptyState Molecule - Universal Empty State Display
|
|
3
|
-
*
|
|
4
|
-
* Displays icon, title, and subtitle for empty data scenarios
|
|
5
|
-
* Theme: {{THEME_NAME}} ({{CATEGORY}} category)
|
|
6
|
-
*
|
|
7
|
-
* Atomic Design Level: MOLECULE
|
|
8
|
-
* Composition: Icon + AtomicText + Layout
|
|
9
|
-
*
|
|
10
|
-
* Usage:
|
|
11
|
-
* - Empty lists
|
|
12
|
-
* - Empty grids
|
|
13
|
-
* - No search results
|
|
14
|
-
* - No data states
|
|
15
|
-
*/
|
|
16
|
-
import React from 'react';
|
|
17
|
-
import { View, StyleSheet } from 'react-native';
|
|
18
|
-
import { AtomicText, AtomicIcon } from '../atoms';
|
|
19
|
-
import { useAppDesignTokens } from '@umituz/react-native-theme';
|
|
20
|
-
// =============================================================================
|
|
21
|
-
// COMPONENT IMPLEMENTATION
|
|
22
|
-
// =============================================================================
|
|
23
|
-
export const EmptyState = ({ icon, iconSize = 'xl', title, subtitle, iconColor, titleColor, subtitleColor, style, titleStyle, subtitleStyle, }) => {
|
|
24
|
-
const tokens = useAppDesignTokens();
|
|
25
|
-
const styles = getStyles(tokens);
|
|
26
|
-
return (<View style={[styles.container, style]}>
|
|
27
|
-
<AtomicIcon name={icon} size={iconSize} customColor={iconColor || tokens.colors.textSecondary}/>
|
|
28
|
-
<AtomicText type="headlineMedium" color={titleColor || tokens.colors.textPrimary} style={StyleSheet.flatten([
|
|
29
|
-
styles.title,
|
|
30
|
-
titleStyle,
|
|
31
|
-
])}>
|
|
32
|
-
{title}
|
|
33
|
-
</AtomicText>
|
|
34
|
-
{subtitle && (<AtomicText type="bodyMedium" color={subtitleColor || tokens.colors.textSecondary} style={StyleSheet.flatten([
|
|
35
|
-
styles.subtitle,
|
|
36
|
-
subtitleStyle,
|
|
37
|
-
])}>
|
|
38
|
-
{subtitle}
|
|
39
|
-
</AtomicText>)}
|
|
40
|
-
</View>);
|
|
41
|
-
};
|
|
42
|
-
// =============================================================================
|
|
43
|
-
// STYLES
|
|
44
|
-
// =============================================================================
|
|
45
|
-
const getStyles = (tokens) => StyleSheet.create({
|
|
46
|
-
container: {
|
|
47
|
-
flex: 1,
|
|
48
|
-
justifyContent: 'center',
|
|
49
|
-
alignItems: 'center',
|
|
50
|
-
paddingVertical: tokens.spacing.xl,
|
|
51
|
-
paddingHorizontal: tokens.spacing.lg,
|
|
52
|
-
},
|
|
53
|
-
title: {
|
|
54
|
-
fontSize: tokens.typography.headingMedium.fontSize,
|
|
55
|
-
fontWeight: tokens.typography.headingMedium.fontWeight,
|
|
56
|
-
marginTop: tokens.spacing.md,
|
|
57
|
-
textAlign: 'center',
|
|
58
|
-
},
|
|
59
|
-
subtitle: {
|
|
60
|
-
fontSize: tokens.typography.bodySmall.fontSize,
|
|
61
|
-
marginTop: tokens.spacing.xs,
|
|
62
|
-
textAlign: 'center',
|
|
63
|
-
},
|
|
64
|
-
});
|
|
65
|
-
// =============================================================================
|
|
66
|
-
// EXPORTS
|
|
67
|
-
// =============================================================================
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* FormField Molecule - Complete Form Input with Label and Error
|
|
3
|
-
*
|
|
4
|
-
* Combines AtomicText (label/error) + AtomicInput (field)
|
|
5
|
-
* Theme: {{THEME_NAME}} ({{CATEGORY}} category)
|
|
6
|
-
*
|
|
7
|
-
* Atomic Design Level: MOLECULE
|
|
8
|
-
* Composition: AtomicText + AtomicInput
|
|
9
|
-
*/
|
|
10
|
-
import React from 'react';
|
|
11
|
-
import { View } from 'react-native';
|
|
12
|
-
import { useAppDesignTokens } from '@umituz/react-native-theme';
|
|
13
|
-
import { AtomicText } from '../atoms/AtomicText';
|
|
14
|
-
import { AtomicInput } from '../atoms/AtomicInput';
|
|
15
|
-
// =============================================================================
|
|
16
|
-
// COMPONENT IMPLEMENTATION
|
|
17
|
-
// =============================================================================
|
|
18
|
-
export const FormField = ({ label, error, helperText, required = false, containerStyle, style, // Accept both style and containerStyle
|
|
19
|
-
...inputProps }) => {
|
|
20
|
-
const tokens = useAppDesignTokens();
|
|
21
|
-
const inputState = error ? 'error' : 'default';
|
|
22
|
-
const styles = getStyles(tokens);
|
|
23
|
-
return (<View style={[styles.container, containerStyle || style]}>
|
|
24
|
-
{/* Label */}
|
|
25
|
-
{label && (<View style={styles.labelContainer}>
|
|
26
|
-
<AtomicText type="labelMedium" color="primary" style={styles.label}>
|
|
27
|
-
{label}
|
|
28
|
-
</AtomicText>
|
|
29
|
-
{required && (<AtomicText type="labelMedium" color="error">
|
|
30
|
-
{' *'}
|
|
31
|
-
</AtomicText>)}
|
|
32
|
-
</View>)}
|
|
33
|
-
|
|
34
|
-
{/* Input Field */}
|
|
35
|
-
<AtomicInput {...inputProps} label={label || ''} state={inputState}/>
|
|
36
|
-
|
|
37
|
-
{/* Error Message */}
|
|
38
|
-
{error && (<AtomicText type="bodySmall" color="error" style={styles.errorText}>
|
|
39
|
-
{error}
|
|
40
|
-
</AtomicText>)}
|
|
41
|
-
|
|
42
|
-
{/* Helper Text */}
|
|
43
|
-
{!error && helperText && (<AtomicText type="bodySmall" color="secondary" style={styles.helperText}>
|
|
44
|
-
{helperText}
|
|
45
|
-
</AtomicText>)}
|
|
46
|
-
</View>);
|
|
47
|
-
};
|
|
48
|
-
// =============================================================================
|
|
49
|
-
// STYLES
|
|
50
|
-
// =============================================================================
|
|
51
|
-
const getStyles = (tokens) => ({
|
|
52
|
-
container: {
|
|
53
|
-
marginBottom: tokens.spacing.md,
|
|
54
|
-
},
|
|
55
|
-
labelContainer: {
|
|
56
|
-
flexDirection: 'row',
|
|
57
|
-
marginBottom: tokens.spacing.sm,
|
|
58
|
-
},
|
|
59
|
-
label: {
|
|
60
|
-
fontWeight: tokens.typography.labelMedium.fontWeight,
|
|
61
|
-
color: tokens.colors.textPrimary,
|
|
62
|
-
},
|
|
63
|
-
inputError: {
|
|
64
|
-
borderColor: tokens.colors.error,
|
|
65
|
-
},
|
|
66
|
-
errorText: {
|
|
67
|
-
marginTop: tokens.spacing.xs,
|
|
68
|
-
},
|
|
69
|
-
helperText: {
|
|
70
|
-
marginTop: tokens.spacing.xs,
|
|
71
|
-
},
|
|
72
|
-
});
|
|
73
|
-
// =============================================================================
|
|
74
|
-
// EXPORTS
|
|
75
|
-
// =============================================================================
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GridContainer Molecule - Responsive Grid Layout
|
|
3
|
-
*
|
|
4
|
-
* Provides flexible grid layout with configurable columns and gap
|
|
5
|
-
* Theme: {{THEME_NAME}} ({{CATEGORY}} category)
|
|
6
|
-
*
|
|
7
|
-
* Atomic Design Level: MOLECULE
|
|
8
|
-
* Composition: View + Responsive Layout
|
|
9
|
-
*
|
|
10
|
-
* Usage:
|
|
11
|
-
* - Stats grids (2 columns)
|
|
12
|
-
* - Action grids (2 columns)
|
|
13
|
-
* - Product grids (2-3 columns)
|
|
14
|
-
* - Gallery grids (3-4 columns)
|
|
15
|
-
*/
|
|
16
|
-
import React from 'react';
|
|
17
|
-
import { View, StyleSheet } from 'react-native';
|
|
18
|
-
// =============================================================================
|
|
19
|
-
// COMPONENT IMPLEMENTATION
|
|
20
|
-
// =============================================================================
|
|
21
|
-
const GridContainerComponent = ({ columns = 2, gap = 8, style, children, }) => {
|
|
22
|
-
const styles = getStyles(gap);
|
|
23
|
-
return (<View style={[styles.container, style]}>
|
|
24
|
-
{React.Children.map(children, (child) => {
|
|
25
|
-
if (React.isValidElement(child)) {
|
|
26
|
-
const childStyle = child.props.style;
|
|
27
|
-
const itemStyle = getItemStyle(columns, gap);
|
|
28
|
-
return React.cloneElement(child, {
|
|
29
|
-
style: StyleSheet.flatten([itemStyle, childStyle]),
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
return child;
|
|
33
|
-
})}
|
|
34
|
-
</View>);
|
|
35
|
-
};
|
|
36
|
-
// =============================================================================
|
|
37
|
-
// GRID ITEM COMPONENT
|
|
38
|
-
// =============================================================================
|
|
39
|
-
export const GridItem = ({ children, style }) => {
|
|
40
|
-
return <View style={style}>{children}</View>;
|
|
41
|
-
};
|
|
42
|
-
// Export GridContainer with Item property
|
|
43
|
-
export const GridContainer = GridContainerComponent;
|
|
44
|
-
// Attach GridItem to GridContainer for convenient usage
|
|
45
|
-
GridContainer.Item = GridItem;
|
|
46
|
-
// =============================================================================
|
|
47
|
-
// HELPERS
|
|
48
|
-
// =============================================================================
|
|
49
|
-
const getItemStyle = (columns, gap) => {
|
|
50
|
-
// Calculate width: (100% - total gap space) / columns
|
|
51
|
-
// For 2 columns with 8px gap: (100% - 8px) / 2 = ~48%
|
|
52
|
-
// For 3 columns with 8px gap: (100% - 16px) / 3 = ~31.33%
|
|
53
|
-
const widthMap = {
|
|
54
|
-
2: '48%',
|
|
55
|
-
3: '31.33%',
|
|
56
|
-
4: '23%',
|
|
57
|
-
};
|
|
58
|
-
return {
|
|
59
|
-
width: widthMap[columns] || '48%',
|
|
60
|
-
marginHorizontal: gap / 2,
|
|
61
|
-
marginBottom: gap * 1.5,
|
|
62
|
-
};
|
|
63
|
-
};
|
|
64
|
-
// =============================================================================
|
|
65
|
-
// STYLES
|
|
66
|
-
// =============================================================================
|
|
67
|
-
const getStyles = (gap) => StyleSheet.create({
|
|
68
|
-
container: {
|
|
69
|
-
flexDirection: 'row',
|
|
70
|
-
flexWrap: 'wrap',
|
|
71
|
-
marginHorizontal: -(gap / 2),
|
|
72
|
-
},
|
|
73
|
-
});
|
|
74
|
-
// =============================================================================
|
|
75
|
-
// EXPORTS
|
|
76
|
-
// =============================================================================
|