@umituz/react-native-design-system 4.28.13 → 4.28.14

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": "4.28.13",
3
+ "version": "4.28.14",
4
4
  "description": "Universal design system for React Native apps with safe navigation hooks - updated SKILL.md with navigation documentation",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./dist/index.d.ts",
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import React, { useMemo } from "react";
9
- import { View, StyleSheet, type ViewStyle, type StyleProp } from "react-native";
9
+ import { View, type ViewStyle, type StyleProp } from "react-native";
10
10
  import { AtomicText } from "../../atoms/AtomicText";
11
11
  import { useAppDesignTokens } from "../../theme/hooks/useAppDesignTokens";
12
12
  import {
@@ -60,52 +60,51 @@ export const StepHeader: React.FC<StepHeaderProps> = ({
60
60
  const spacingMultiplier = tokens.spacingMultiplier;
61
61
 
62
62
  const styles = useMemo(
63
- () =>
64
- StyleSheet.create({
65
- container: {
66
- paddingHorizontal: calculateResponsiveSize(
67
- cfg.spacing?.paddingHorizontal ?? SPACING.xl,
68
- spacingMultiplier
69
- ),
70
- marginBottom: calculateResponsiveSize(
71
- cfg.spacing?.marginBottom ?? 32,
72
- spacingMultiplier
73
- ),
74
- },
75
- stepIndicator: {
76
- flexDirection: "row",
77
- alignItems: "center",
78
- marginBottom: tokens.spacing.md,
79
- },
80
- stepDot: {
81
- width: calculateResponsiveSize(STEP_INDICATOR.dot.width, spacingMultiplier),
82
- height: calculateResponsiveSize(STEP_INDICATOR.dot.height, spacingMultiplier),
83
- borderRadius: calculateResponsiveSize(STEP_INDICATOR.dot.borderRadius, spacingMultiplier),
84
- marginHorizontal: calculateResponsiveSize(STEP_INDICATOR.dot.marginHorizontal, spacingMultiplier),
85
- },
86
- activeDot: {
87
- backgroundColor: tokens.colors.primary,
88
- },
89
- inactiveDot: {
90
- backgroundColor: `${tokens.colors.primary}30`,
91
- },
92
- title: {
93
- fontSize: calculateResponsiveSize(cfg.titleFontSize ?? STEP_INDICATOR.title, spacingMultiplier),
94
- fontWeight: "900",
95
- color: tokens.colors.textPrimary,
96
- textAlign: cfg.titleAlignment,
97
- marginBottom: subtitle ? calculateResponsiveSize(STEP_INDICATOR.subtitle, spacingMultiplier) : 0,
98
- letterSpacing: 0.3,
99
- },
100
- subtitle: {
101
- fontSize: calculateResponsiveSize(cfg.subtitleFontSize ?? STEP_INDICATOR.subtitle, spacingMultiplier),
102
- fontWeight: "500",
103
- color: tokens.colors.textSecondary,
104
- textAlign: cfg.titleAlignment,
105
- lineHeight: calculateLineHeight(cfg.subtitleFontSize ?? STEP_INDICATOR.subtitle, spacingMultiplier),
106
- opacity: 0.9,
107
- },
108
- }),
63
+ () => ({
64
+ container: {
65
+ paddingHorizontal: calculateResponsiveSize(
66
+ cfg.spacing?.paddingHorizontal ?? SPACING.xl,
67
+ spacingMultiplier
68
+ ),
69
+ marginBottom: calculateResponsiveSize(
70
+ cfg.spacing?.marginBottom ?? 32,
71
+ spacingMultiplier
72
+ ),
73
+ },
74
+ stepIndicator: {
75
+ flexDirection: "row" as const,
76
+ alignItems: "center" as const,
77
+ marginBottom: tokens.spacing.md,
78
+ },
79
+ stepDot: {
80
+ width: calculateResponsiveSize(STEP_INDICATOR.dot.width, spacingMultiplier),
81
+ height: calculateResponsiveSize(STEP_INDICATOR.dot.height, spacingMultiplier),
82
+ borderRadius: calculateResponsiveSize(STEP_INDICATOR.dot.borderRadius, spacingMultiplier),
83
+ marginHorizontal: calculateResponsiveSize(STEP_INDICATOR.dot.marginHorizontal, spacingMultiplier),
84
+ },
85
+ activeDot: {
86
+ backgroundColor: tokens.colors.primary,
87
+ },
88
+ inactiveDot: {
89
+ backgroundColor: `${tokens.colors.primary}30`,
90
+ },
91
+ title: {
92
+ fontSize: calculateResponsiveSize(cfg.titleFontSize ?? STEP_INDICATOR.title, spacingMultiplier),
93
+ fontWeight: "900" as const,
94
+ color: tokens.colors.textPrimary,
95
+ textAlign: cfg.titleAlignment,
96
+ marginBottom: subtitle ? calculateResponsiveSize(STEP_INDICATOR.subtitle, spacingMultiplier) : 0,
97
+ letterSpacing: 0.3,
98
+ },
99
+ subtitle: {
100
+ fontSize: calculateResponsiveSize(cfg.subtitleFontSize ?? STEP_INDICATOR.subtitle, spacingMultiplier),
101
+ fontWeight: "500" as const,
102
+ color: tokens.colors.textSecondary,
103
+ textAlign: cfg.titleAlignment,
104
+ lineHeight: calculateLineHeight(cfg.subtitleFontSize ?? STEP_INDICATOR.subtitle, spacingMultiplier),
105
+ opacity: 0.9,
106
+ },
107
+ }),
109
108
  [tokens, cfg, subtitle, spacingMultiplier],
110
109
  );
111
110
 
@@ -1,5 +1,5 @@
1
1
  import React, { useMemo } from "react";
2
- import { View, StyleSheet, ViewStyle } from "react-native";
2
+ import { View, ViewStyle } from "react-native";
3
3
  import { useAppDesignTokens } from '../../theme/hooks/useAppDesignTokens';
4
4
  import { calculateResponsiveSize } from '../../responsive';
5
5
  import { STEP_INDICATOR } from '../../constants';
@@ -20,24 +20,23 @@ export const StepProgress: React.FC<StepProgressProps> = ({
20
20
  const spacingMultiplier = tokens.spacingMultiplier;
21
21
 
22
22
  const styles = useMemo(
23
- () =>
24
- StyleSheet.create({
25
- container: {
26
- flexDirection: "row",
27
- gap: tokens.spacing.sm,
28
- paddingHorizontal: tokens.spacing.md,
29
- paddingVertical: tokens.spacing.md,
30
- },
31
- step: {
32
- flex: 1,
33
- height: calculateResponsiveSize(STEP_INDICATOR.progressBar.height, spacingMultiplier),
34
- borderRadius: calculateResponsiveSize(STEP_INDICATOR.progressBar.borderRadius, spacingMultiplier),
35
- backgroundColor: tokens.colors.border,
36
- },
37
- activeStep: {
38
- backgroundColor: tokens.colors.primary,
39
- },
40
- }),
23
+ () => ({
24
+ container: {
25
+ flexDirection: "row" as const,
26
+ gap: tokens.spacing.sm,
27
+ paddingHorizontal: tokens.spacing.md,
28
+ paddingVertical: tokens.spacing.md,
29
+ },
30
+ step: {
31
+ flex: 1,
32
+ height: calculateResponsiveSize(STEP_INDICATOR.progressBar.height, spacingMultiplier),
33
+ borderRadius: calculateResponsiveSize(STEP_INDICATOR.progressBar.borderRadius, spacingMultiplier),
34
+ backgroundColor: tokens.colors.border,
35
+ },
36
+ activeStep: {
37
+ backgroundColor: tokens.colors.primary,
38
+ },
39
+ }),
41
40
  [tokens, spacingMultiplier],
42
41
  );
43
42
 
@@ -1,6 +1,6 @@
1
1
 
2
2
  import React, { useMemo, useCallback } from 'react';
3
- import { View, StyleSheet, TouchableOpacity } from 'react-native';
3
+ import { View, TouchableOpacity } from 'react-native';
4
4
  import { AtomicText } from '../../atoms/AtomicText';
5
5
  import { AtomicIcon } from '../../atoms';
6
6
  import { useAppDesignTokens } from '../../theme';
@@ -8,10 +8,10 @@ import type { ActionFooterProps } from './types';
8
8
  import { calculateResponsiveSize } from '../../responsive';
9
9
  import { NAVIGATION } from '../../constants';
10
10
 
11
- const createStyles = (spacingMultiplier: number) => StyleSheet.create({
11
+ const createStyles = (spacingMultiplier: number) => ({
12
12
  container: {
13
- flexDirection: 'row',
14
- alignItems: 'center',
13
+ flexDirection: 'row' as const,
14
+ alignItems: 'center' as const,
15
15
  paddingVertical: 0,
16
16
  gap: 0,
17
17
  },
@@ -20,8 +20,8 @@ const createStyles = (spacingMultiplier: number) => StyleSheet.create({
20
20
  height: calculateResponsiveSize(NAVIGATION.backButton.height, spacingMultiplier),
21
21
  borderRadius: 0,
22
22
  backgroundColor: '',
23
- justifyContent: 'center',
24
- alignItems: 'center',
23
+ justifyContent: 'center' as const,
24
+ alignItems: 'center' as const,
25
25
  borderWidth: 1,
26
26
  borderColor: '',
27
27
  },
@@ -29,13 +29,13 @@ const createStyles = (spacingMultiplier: number) => StyleSheet.create({
29
29
  flex: 1,
30
30
  height: calculateResponsiveSize(NAVIGATION.backButton.height, spacingMultiplier),
31
31
  borderRadius: 0,
32
- overflow: 'hidden',
32
+ overflow: 'hidden' as const,
33
33
  },
34
34
  actionContent: {
35
35
  flex: 1,
36
- flexDirection: 'row',
37
- alignItems: 'center',
38
- justifyContent: 'flex-start',
36
+ flexDirection: 'row' as const,
37
+ alignItems: 'center' as const,
38
+ justifyContent: 'flex-start' as const,
39
39
  backgroundColor: '',
40
40
  gap: 0,
41
41
  paddingHorizontal: 0,
@@ -2,8 +2,8 @@
2
2
  * AlertModal Component
3
3
  */
4
4
 
5
- import React, { useMemo } from 'react';
6
- import { StyleSheet, View, Modal, Pressable } from 'react-native';
5
+ import React, { useMemo, useCallback } from 'react';
6
+ import { View, Modal, Pressable } from 'react-native';
7
7
  import { AtomicButton, AtomicText, AtomicIcon } from '../../atoms';
8
8
  import { useAppDesignTokens } from '../../theme';
9
9
  import { Alert, AlertType } from './AlertTypes';
@@ -35,43 +35,54 @@ export const AlertModal: React.FC<AlertModalProps> = ({ alert }) => {
35
35
  const iconName = getAlertIconName(alert.type);
36
36
  const hasTwoActions = alert.actions.length === 2;
37
37
 
38
- const styles = useMemo(() => StyleSheet.create({
38
+ const handleActionPress = useCallback(async (action: typeof alert.actions[0]) => {
39
+ await action.onPress();
40
+ if (action.closeOnPress ?? true) {
41
+ handleClose();
42
+ }
43
+ }, [handleClose]);
44
+
45
+ const styles = useMemo(() => ({
39
46
  overlay: {
40
47
  flex: 1,
41
- justifyContent: 'center',
42
- alignItems: 'center',
48
+ justifyContent: 'center' as const,
49
+ alignItems: 'center' as const,
43
50
  padding: calculateResponsiveSize(MODAL_SIZES.overlayPadding, spacingMultiplier),
44
51
  },
45
52
  backdrop: {
46
- ...StyleSheet.absoluteFillObject,
53
+ position: 'absolute' as const,
54
+ top: 0,
55
+ left: 0,
56
+ right: 0,
57
+ bottom: 0,
47
58
  backgroundColor: 'rgba(0,0,0,0.55)',
48
59
  },
49
60
  modal: {
50
61
  width: '100%',
51
62
  maxWidth: calculateResponsiveSize(MODAL_SIZES.maxWidth, spacingMultiplier),
52
63
  padding: calculateResponsiveSize(MODAL_SIZES.padding, spacingMultiplier),
53
- alignItems: 'center',
64
+ alignItems: 'center' as const,
54
65
  },
55
66
  iconCircle: {
56
67
  width: calculateResponsiveSize(ALERT_MODAL_ICON.width, spacingMultiplier),
57
68
  height: calculateResponsiveSize(ALERT_MODAL_ICON.height, spacingMultiplier),
58
69
  borderRadius: calculateResponsiveSize(ALERT_MODAL_ICON.borderRadius, spacingMultiplier),
59
- justifyContent: 'center',
60
- alignItems: 'center',
70
+ justifyContent: 'center' as const,
71
+ alignItems: 'center' as const,
61
72
  marginBottom: calculateResponsiveSize(ALERT_MODAL_ICON.marginBottom, spacingMultiplier),
62
73
  },
63
74
  title: {
64
- fontWeight: '700',
65
- textAlign: 'center',
75
+ fontWeight: '700' as const,
76
+ textAlign: 'center' as const,
66
77
  marginBottom: tokens.spacing.sm,
67
78
  },
68
79
  message: {
69
- textAlign: 'center',
80
+ textAlign: 'center' as const,
70
81
  lineHeight: calculateResponsiveSize(24, spacingMultiplier),
71
82
  opacity: 0.85,
72
83
  },
73
84
  actionsRow: {
74
- flexDirection: 'row',
85
+ flexDirection: 'row' as const,
75
86
  width: '100%',
76
87
  },
77
88
  actionsColumn: {
@@ -170,12 +181,7 @@ export const AlertModal: React.FC<AlertModalProps> = ({ alert }) => {
170
181
  : action.style === 'secondary' ? 'outline'
171
182
  : 'primary'
172
183
  }
173
- onPress={async () => {
174
- await action.onPress();
175
- if (action.closeOnPress ?? true) {
176
- handleClose();
177
- }
178
- }}
184
+ onPress={() => handleActionPress(action)}
179
185
  fullWidth={!hasTwoActions}
180
186
  style={hasTwoActions ? styles.actionButtonHalf : undefined}
181
187
  />
@@ -56,6 +56,10 @@ export const BottomSheet = forwardRef<BottomSheetRef, BottomSheetProps>((props,
56
56
  onChange?.(-1);
57
57
  }, [onClose, onChange]);
58
58
 
59
+ const handleContentPress = useCallback((e: any) => {
60
+ e.stopPropagation();
61
+ }, []);
62
+
59
63
  useImperativeHandle(ref, () => ({
60
64
  snapToIndex: (index: number) => {
61
65
  if (index >= 0) present();
@@ -107,7 +111,7 @@ export const BottomSheet = forwardRef<BottomSheetRef, BottomSheetProps>((props,
107
111
  >
108
112
  <Pressable style={styles.overlay} onPress={dismiss} accessibilityLabel="Close" accessibilityRole="button">
109
113
  <View style={styles.container}>
110
- <Pressable onPress={(e) => e.stopPropagation()} style={styles.content} accessibilityRole="none">
114
+ <Pressable onPress={handleContentPress} style={styles.content} accessibilityRole="none">
111
115
  <View style={styles.handle} />
112
116
  {children}
113
117
  </Pressable>
@@ -55,11 +55,11 @@ export const BottomSheetModal = forwardRef<BottomSheetModalRef, BottomSheetModal
55
55
 
56
56
  const spacingMultiplier = tokens.spacingMultiplier;
57
57
 
58
- const styles = useMemo(() => StyleSheet.create({
58
+ const styles = useMemo(() => ({
59
59
  overlay: {
60
60
  flex: 1,
61
61
  backgroundColor: tokens.colors.modalOverlay,
62
- justifyContent: 'flex-end',
62
+ justifyContent: 'flex-end' as const,
63
63
  },
64
64
  container: {
65
65
  height: sheetHeight,
@@ -67,19 +67,20 @@ export const BottomSheetModal = forwardRef<BottomSheetModalRef, BottomSheetModal
67
67
  borderTopLeftRadius: borderRadius,
68
68
  borderTopRightRadius: borderRadius,
69
69
  paddingBottom: Math.max(insets.bottom, tokens.spacing.xs),
70
+ width: '100%' as const,
71
+ },
72
+ contentWrapper: {
73
+ flex: 1,
70
74
  },
71
75
  handle: {
72
76
  width: calculateResponsiveSize(BOTTOM_SHEET_HANDLE.width, spacingMultiplier),
73
77
  height: calculateResponsiveSize(BOTTOM_SHEET_HANDLE.height, spacingMultiplier),
74
78
  backgroundColor: tokens.colors.border,
75
79
  borderRadius: calculateResponsiveSize(BOTTOM_SHEET_HANDLE.borderRadius, spacingMultiplier),
76
- alignSelf: 'center',
80
+ alignSelf: 'center' as const,
77
81
  marginTop: tokens.spacing.md,
78
82
  marginBottom: tokens.spacing.sm,
79
83
  },
80
- content: {
81
- flex: 1,
82
- },
83
84
  }), [sheetHeight, backgroundColor, tokens, borderRadius, insets.bottom, spacingMultiplier]);
84
85
 
85
86
  return (
@@ -91,13 +92,13 @@ export const BottomSheetModal = forwardRef<BottomSheetModalRef, BottomSheetModal
91
92
  statusBarTranslucent
92
93
  >
93
94
  <View style={styles.overlay}>
94
- <Pressable
95
- style={StyleSheet.absoluteFill}
95
+ <Pressable
96
+ style={StyleSheet.absoluteFill}
96
97
  onPress={dismiss}
97
98
  accessibilityLabel="Close modal"
98
99
  />
99
- <View style={[styles.container, { width: '100%' }]}>
100
- <View style={{ flex: 1 }}>
100
+ <View style={styles.container}>
101
+ <View style={styles.contentWrapper}>
101
102
  <View style={styles.handle} />
102
103
  {children}
103
104
  </View>
@@ -1,5 +1,5 @@
1
1
  import React, { useMemo } from "react";
2
- import { View, StyleSheet, TouchableOpacity } from "react-native";
2
+ import { View, TouchableOpacity } from "react-native";
3
3
  import { AtomicIcon } from "../../atoms";
4
4
  import { AtomicText } from "../../atoms";
5
5
  import { useAppDesignTokens } from "../../theme";
@@ -20,22 +20,29 @@ export const CircularMenuItem: React.FC<CircularMenuItemProps> = React.memo(({
20
20
  const tokens = useAppDesignTokens();
21
21
  const spacingMultiplier = tokens.spacingMultiplier;
22
22
 
23
- const styles = useMemo(() => StyleSheet.create({
23
+ const styles = useMemo(() => ({
24
24
  container: {
25
- alignItems: "center",
25
+ alignItems: "center" as const,
26
26
  gap: 6,
27
27
  width: LAYOUT.ITEM_SIZE,
28
28
  },
29
29
  iconContainer: {
30
- justifyContent: "center",
31
- alignItems: "center",
30
+ justifyContent: "center" as const,
31
+ alignItems: "center" as const,
32
+ backgroundColor: tokens.colors.surfaceVariant,
33
+ width: LAYOUT.ICON_SIZE,
34
+ height: LAYOUT.ICON_SIZE,
35
+ borderRadius: LAYOUT.ICON_SIZE / 2,
36
+ borderWidth: 1,
37
+ borderColor: tokens.colors.border,
32
38
  },
33
39
  label: {
34
40
  fontSize: calculateResponsiveSize(11, spacingMultiplier),
35
- fontWeight: "500",
36
- textAlign: "center",
41
+ fontWeight: "500" as const,
42
+ textAlign: "center" as const,
43
+ color: tokens.colors.textPrimary,
37
44
  },
38
- }), [spacingMultiplier]);
45
+ }), [spacingMultiplier, tokens.colors.surfaceVariant, tokens.colors.border, tokens.colors.textPrimary]);
39
46
 
40
47
  return (
41
48
  <TouchableOpacity
@@ -45,24 +52,12 @@ export const CircularMenuItem: React.FC<CircularMenuItemProps> = React.memo(({
45
52
  accessibilityRole="button"
46
53
  accessibilityLabel={label}
47
54
  >
48
- <View
49
- style={[
50
- styles.iconContainer,
51
- {
52
- backgroundColor: tokens.colors.surfaceVariant,
53
- width: LAYOUT.ICON_SIZE,
54
- height: LAYOUT.ICON_SIZE,
55
- borderRadius: LAYOUT.ICON_SIZE / 2,
56
- borderWidth: 1,
57
- borderColor: tokens.colors.border,
58
- },
59
- ]}
60
- >
55
+ <View style={styles.iconContainer}>
61
56
  <AtomicIcon name={icon} size="lg" color="primary" />
62
57
  </View>
63
58
  <AtomicText
64
59
  type="labelSmall"
65
- style={[styles.label, { color: tokens.colors.textPrimary }]}
60
+ style={styles.label}
66
61
  >
67
62
  {label}
68
63
  </AtomicText>
@@ -30,9 +30,29 @@ export const CountdownHeader: React.FC<CountdownHeaderProps> = ({
30
30
  [spacingMultiplier]
31
31
  );
32
32
 
33
+ const containerStyle = useMemo(() => [
34
+ styles.container,
35
+ { marginBottom: tokens.spacing.md },
36
+ ], [tokens.spacing.md]);
37
+
38
+ const titleRowStyle = useMemo(() => [
39
+ styles.titleRow,
40
+ { gap: tokens.spacing.sm },
41
+ ], [tokens.spacing.sm]);
42
+
43
+ const toggleButtonStyle = useMemo(() => [
44
+ styles.toggleButton,
45
+ {
46
+ backgroundColor: tokens.colors.surfaceSecondary,
47
+ width: toggleButtonSize,
48
+ height: toggleButtonSize,
49
+ borderRadius: toggleButtonSize / 2,
50
+ },
51
+ ], [styles.toggleButton, tokens.colors.surfaceSecondary, toggleButtonSize]);
52
+
33
53
  return (
34
- <View style={[styles.container, { marginBottom: tokens.spacing.md }]}>
35
- <View style={[styles.titleRow, { gap: tokens.spacing.sm }]}>
54
+ <View style={containerStyle}>
55
+ <View style={titleRowStyle}>
36
56
  {icon && (
37
57
  <AtomicIcon
38
58
  name={icon}
@@ -51,15 +71,7 @@ export const CountdownHeader: React.FC<CountdownHeaderProps> = ({
51
71
 
52
72
  {showToggle && onToggle && (
53
73
  <TouchableOpacity
54
- style={[
55
- styles.toggleButton,
56
- {
57
- backgroundColor: tokens.colors.surfaceSecondary,
58
- width: toggleButtonSize,
59
- height: toggleButtonSize,
60
- borderRadius: toggleButtonSize / 2,
61
- },
62
- ]}
74
+ style={toggleButtonStyle}
63
75
  onPress={onToggle}
64
76
  accessibilityRole="button"
65
77
  accessibilityLabel="Toggle view"
@@ -1,5 +1,5 @@
1
1
  import React, { useMemo } from 'react';
2
- import { View, StyleSheet } from 'react-native';
2
+ import { View } from 'react-native';
3
3
  import { AtomicText } from '../../../atoms';
4
4
  import { useAppDesignTokens } from '../../../theme';
5
5
  import { calculateResponsiveSize } from '../../../responsive';
@@ -19,25 +19,25 @@ export const TimeUnit: React.FC<TimeUnitProps> = ({
19
19
  const tokens = useAppDesignTokens();
20
20
  const spacingMultiplier = tokens.spacingMultiplier;
21
21
 
22
- const styles = useMemo(() => StyleSheet.create({
22
+ const styles = useMemo(() => ({
23
23
  container: {
24
24
  flex: 1,
25
- alignItems: 'center',
26
- justifyContent: 'center',
25
+ alignItems: 'center' as const,
26
+ justifyContent: 'center' as const,
27
27
  },
28
28
  value: {
29
- fontWeight: '700',
29
+ fontWeight: '700' as const,
30
30
  lineHeight: calculateResponsiveSize(38, spacingMultiplier),
31
31
  },
32
32
  label: {
33
- fontWeight: '600',
33
+ fontWeight: '600' as const,
34
34
  marginTop: calculateResponsiveSize(2, spacingMultiplier),
35
35
  letterSpacing: 1,
36
- textTransform: 'uppercase',
36
+ textTransform: 'uppercase' as const,
37
37
  },
38
38
  }), [spacingMultiplier]);
39
39
 
40
- const sizeConfig = {
40
+ const sizeConfig = useMemo(() => ({
41
41
  small: {
42
42
  fontSize: calculateResponsiveSize(COUNTDOWN_SIZES.small.fontSize, spacingMultiplier),
43
43
  padding: tokens.spacing.sm,
@@ -53,7 +53,7 @@ export const TimeUnit: React.FC<TimeUnitProps> = ({
53
53
  padding: tokens.spacing.lg,
54
54
  minHeight: calculateResponsiveSize(COUNTDOWN_SIZES.large.minHeight, spacingMultiplier),
55
55
  },
56
- };
56
+ }), [spacingMultiplier, tokens.spacing.sm, tokens.spacing.md, tokens.spacing.lg]);
57
57
 
58
58
  const config = sizeConfig[size];
59
59
 
@@ -70,23 +70,28 @@ export const TimeUnit: React.FC<TimeUnitProps> = ({
70
70
 
71
71
  const calculatedFontSize = config.fontSize * fontSizeMultiplier;
72
72
 
73
+ const containerStyle = useMemo(() => [
74
+ styles.container,
75
+ {
76
+ backgroundColor: tokens.colors.surfaceSecondary,
77
+ borderRadius: tokens.borders.radius.lg,
78
+ paddingVertical: config.padding,
79
+ minHeight: config.minHeight,
80
+ paddingHorizontal: tokens.spacing.xs,
81
+ },
82
+ ], [styles.container, tokens.colors.surfaceSecondary, tokens.borders.radius.lg, config.padding, config.minHeight, tokens.spacing.xs]);
83
+
84
+ const valueStyle = useMemo(() => [
85
+ styles.value,
86
+ { fontSize: calculatedFontSize },
87
+ ], [styles.value, calculatedFontSize]);
88
+
73
89
  return (
74
- <View
75
- style={[
76
- styles.container,
77
- {
78
- backgroundColor: tokens.colors.surfaceSecondary,
79
- borderRadius: tokens.borders.radius.lg,
80
- paddingVertical: config.padding,
81
- minHeight: config.minHeight,
82
- paddingHorizontal: tokens.spacing.xs,
83
- },
84
- ]}
85
- >
90
+ <View style={containerStyle}>
86
91
  <AtomicText
87
92
  type="displaySmall"
88
93
  color="onSurface"
89
- style={[styles.value, { fontSize: calculatedFontSize }]}
94
+ style={valueStyle}
90
95
  numberOfLines={1}
91
96
  >
92
97
  {displayValue}
@@ -1,6 +1,6 @@
1
1
 
2
2
  import React, { useMemo } from 'react';
3
- import { View, StyleSheet, Text, Image } from 'react-native';
3
+ import { View, Text, Image } from 'react-native';
4
4
  import { useAppDesignTokens } from '../../theme';
5
5
  import type { HeroSectionProps } from './types';
6
6
  import { calculateResponsiveSize } from '../../responsive';
@@ -28,7 +28,11 @@ export const HeroSection: React.FC<HeroSectionProps> = ({
28
28
  overflow: 'hidden' as const,
29
29
  },
30
30
  background: {
31
- ...StyleSheet.absoluteFillObject,
31
+ position: 'absolute' as const,
32
+ top: 0,
33
+ left: 0,
34
+ right: 0,
35
+ bottom: 0,
32
36
  backgroundColor: tokens.colors.surfaceVariant,
33
37
  },
34
38
  iconWrapper: {
@@ -46,19 +50,23 @@ export const HeroSection: React.FC<HeroSectionProps> = ({
46
50
  [tokens, height, spacingMultiplier],
47
51
  );
48
52
 
49
- const styles = useMemo(() => StyleSheet.create({
53
+ const styles = useMemo(() => ({
50
54
  image: {
51
- ...StyleSheet.absoluteFillObject,
55
+ position: 'absolute' as const,
56
+ top: 0,
57
+ left: 0,
58
+ right: 0,
59
+ bottom: 0,
52
60
  width: '100%',
53
61
  height: '100%',
54
62
  },
55
63
  emoji: {
56
64
  fontSize: calculateResponsiveSize(64, spacingMultiplier),
57
- textAlign: 'left',
65
+ textAlign: 'left' as const,
58
66
  includeFontPadding: false,
59
67
  },
60
68
  fadeOverlay: {
61
- position: 'absolute',
69
+ position: 'absolute' as const,
62
70
  bottom: -1,
63
71
  left: 0,
64
72
  right: 0,
@@ -10,7 +10,6 @@ import React, { useCallback, useMemo, useState } from 'react';
10
10
  import {
11
11
  View,
12
12
  TouchableOpacity,
13
- StyleSheet,
14
13
  type LayoutChangeEvent,
15
14
  type StyleProp,
16
15
  type ViewStyle,
@@ -152,7 +151,7 @@ export const IconGrid = React.memo<IconGridProps>(({
152
151
  // Stable renderItem with memoization
153
152
  const renderItem = useCallback(({ item }: { item: IconGridItem }) => {
154
153
  if (itemWidth === 0) {
155
- return <View key={item.id} style={{ width: 0, height: 0 }} />;
154
+ return <View key={item.id} style={styles.placeholder} />;
156
155
  }
157
156
 
158
157
  return (
@@ -177,7 +176,7 @@ export const IconGrid = React.memo<IconGridProps>(({
177
176
  return (
178
177
  <View style={gridStyle} onLayout={handleLayout}>
179
178
  {items.map((item) => (
180
- <View key={item.id} style={{ width: 0, height: 0 }} />
179
+ <View key={item.id} style={styles.placeholder} />
181
180
  ))}
182
181
  </View>
183
182
  );
@@ -198,25 +197,29 @@ export const IconGrid = React.memo<IconGridProps>(({
198
197
  );
199
198
  });
200
199
 
201
- const createStyles = (spacingMultiplier: number) => StyleSheet.create({
200
+ const createStyles = (spacingMultiplier: number) => ({
202
201
  grid: {
203
- flexDirection: 'row',
204
- flexWrap: 'wrap',
202
+ flexDirection: 'row' as const,
203
+ flexWrap: 'wrap' as const,
205
204
  },
206
205
  card: {
207
- alignItems: 'center',
206
+ alignItems: 'center' as const,
208
207
  gap: 8,
209
208
  },
210
209
  iconBox: {
211
210
  aspectRatio: 1,
212
211
  borderRadius: calculateResponsiveSize(ICON_GRID.borderRadius, spacingMultiplier),
213
- alignItems: 'center',
214
- justifyContent: 'center',
212
+ alignItems: 'center' as const,
213
+ justifyContent: 'center' as const,
215
214
  borderWidth: 1,
216
215
  },
217
216
  label: {
218
217
  fontSize: calculateResponsiveSize(ICON_GRID.fontSize, spacingMultiplier),
219
- fontWeight: '700',
220
- textAlign: 'center',
218
+ fontWeight: '700' as const,
219
+ textAlign: 'center' as const,
220
+ },
221
+ placeholder: {
222
+ width: 0,
223
+ height: 0,
221
224
  },
222
225
  });