@umituz/react-native-settings 1.3.8 → 1.4.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-settings",
3
- "version": "1.3.8",
3
+ "version": "1.4.1",
4
4
  "description": "Settings management for React Native apps - user preferences, theme, language, notifications",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -33,7 +33,6 @@
33
33
  "@umituz/react-native-design-system": "latest",
34
34
  "@umituz/react-native-design-system-theme": "latest",
35
35
  "@umituz/react-native-localization": "latest",
36
- "react-native-paper": "^5.12.3",
37
36
  "expo-linear-gradient": "~14.0.0"
38
37
  },
39
38
  "devDependencies": {
@@ -12,8 +12,7 @@
12
12
  */
13
13
 
14
14
  import React from 'react';
15
- import { View, StyleSheet } from 'react-native';
16
- import { List } from 'react-native-paper';
15
+ import { View, StyleSheet, TouchableOpacity } from 'react-native';
17
16
  import { LinearGradient } from 'expo-linear-gradient';
18
17
  import { AtomicText, AtomicIcon } from '@umituz/react-native-design-system';
19
18
  import { useAppDesignTokens } from '@umituz/react-native-design-system-theme';
@@ -63,114 +62,198 @@ export const SettingItem: React.FC<SettingItemProps> = ({
63
62
  ? (iconGradient as unknown as readonly [string, string, ...string[]])
64
63
  : [tokens.colors.surface, tokens.colors.surface] as const;
65
64
 
66
- // Render gradient icon container for left prop
67
- const renderLeft = (props: any) => (
68
- <View style={styles.leftContainer}>
69
- <LinearGradient
70
- colors={gradientColors}
71
- start={{ x: 0, y: 0 }}
72
- end={{ x: 1, y: 1 }}
73
- style={styles.iconContainer}
65
+ if (onPress) {
66
+ return (
67
+ <TouchableOpacity
68
+ onPress={onPress}
69
+ disabled={disabled}
70
+ activeOpacity={0.7}
71
+ style={[styles.listItem, disabled && styles.disabled]}
72
+ testID={testID}
74
73
  >
75
- <AtomicIcon name={icon} size="md" color="primary" />
76
- </LinearGradient>
77
- </View>
78
- );
79
-
80
- // Render right side content for right prop
81
- const renderRight = (props: any) => {
82
- if (rightElement) {
83
- return <View style={styles.rightContainer}>{rightElement}</View>;
84
- }
85
-
86
- if (value) {
87
- return (
88
- <View style={styles.rightContainer}>
89
- <AtomicText type="bodyMedium" color="secondary" style={styles.value} numberOfLines={2}>
90
- {value}
91
- </AtomicText>
74
+ {/* Left: Gradient Icon */}
75
+ <View style={styles.leftContainer}>
76
+ <LinearGradient
77
+ colors={gradientColors}
78
+ start={{ x: 0, y: 0 }}
79
+ end={{ x: 1, y: 1 }}
80
+ style={styles.iconContainer}
81
+ >
82
+ <AtomicIcon name={icon} size="md" customColor="#FFFFFF" />
83
+ </LinearGradient>
92
84
  </View>
93
- );
94
- }
95
85
 
96
- // Show chevron if onPress exists and not explicitly disabled
97
- if ((showChevron ?? true) && onPress) {
98
- return (
99
- <View style={styles.rightContainer}>
100
- <AtomicIcon name="ChevronRight" size="sm" color="secondary" style={styles.chevron} />
86
+ {/* Center: Title and Description */}
87
+ <View style={styles.contentContainer}>
88
+ <AtomicText
89
+ type="bodyLarge"
90
+ color={disabled ? "textDisabled" : "onSurface"}
91
+ style={styles.title}
92
+ numberOfLines={2}
93
+ ellipsizeMode="tail"
94
+ >
95
+ {title}
96
+ </AtomicText>
97
+ {description && (
98
+ <AtomicText
99
+ type="bodySmall"
100
+ color="textSecondary"
101
+ style={styles.description}
102
+ numberOfLines={2}
103
+ ellipsizeMode="tail"
104
+ >
105
+ {description}
106
+ </AtomicText>
107
+ )}
101
108
  </View>
102
- );
103
- }
104
109
 
105
- return null;
106
- };
110
+ {/* Right: Value, Chevron, or Custom Element */}
111
+ {rightElement ? (
112
+ <View style={styles.rightContainer}>{rightElement}</View>
113
+ ) : value ? (
114
+ <View style={styles.rightContainer}>
115
+ <AtomicText
116
+ type="bodyMedium"
117
+ color="textSecondary"
118
+ style={styles.value}
119
+ numberOfLines={2}
120
+ textAlign="right"
121
+ >
122
+ {value}
123
+ </AtomicText>
124
+ </View>
125
+ ) : (showChevron ?? true) ? (
126
+ <View style={styles.rightContainer}>
127
+ <AtomicIcon
128
+ name="ChevronRight"
129
+ size="sm"
130
+ color="textSecondary"
131
+ style={styles.chevron}
132
+ />
133
+ </View>
134
+ ) : null}
135
+ </TouchableOpacity>
136
+ );
137
+ }
107
138
 
108
139
  return (
109
- <List.Item
110
- title={title}
111
- description={description}
112
- left={renderLeft}
113
- right={renderRight}
114
- onPress={onPress}
115
- disabled={disabled}
140
+ <View
141
+ style={[styles.listItem, disabled && styles.disabled]}
116
142
  testID={testID}
117
- style={styles.listItem}
118
- titleStyle={styles.title}
119
- descriptionStyle={styles.description}
120
- titleNumberOfLines={2}
121
- titleEllipsizeMode="tail"
122
- descriptionNumberOfLines={2}
123
- descriptionEllipsizeMode="tail"
124
- />
143
+ >
144
+ {/* Left: Gradient Icon */}
145
+ <View style={styles.leftContainer}>
146
+ <LinearGradient
147
+ colors={gradientColors}
148
+ start={{ x: 0, y: 0 }}
149
+ end={{ x: 1, y: 1 }}
150
+ style={styles.iconContainer}
151
+ >
152
+ <AtomicIcon name={icon} size="md" customColor="#FFFFFF" />
153
+ </LinearGradient>
154
+ </View>
155
+
156
+ {/* Center: Title and Description */}
157
+ <View style={styles.contentContainer}>
158
+ <AtomicText
159
+ type="bodyLarge"
160
+ color={disabled ? "textDisabled" : "onSurface"}
161
+ style={styles.title}
162
+ numberOfLines={2}
163
+ ellipsizeMode="tail"
164
+ >
165
+ {title}
166
+ </AtomicText>
167
+ {description && (
168
+ <AtomicText
169
+ type="bodySmall"
170
+ color="textSecondary"
171
+ style={styles.description}
172
+ numberOfLines={2}
173
+ ellipsizeMode="tail"
174
+ >
175
+ {description}
176
+ </AtomicText>
177
+ )}
178
+ </View>
179
+
180
+ {/* Right: Value, Chevron, or Custom Element */}
181
+ {rightElement ? (
182
+ <View style={styles.rightContainer}>{rightElement}</View>
183
+ ) : value ? (
184
+ <View style={styles.rightContainer}>
185
+ <AtomicText
186
+ type="bodyMedium"
187
+ color="textSecondary"
188
+ style={styles.value}
189
+ numberOfLines={2}
190
+ textAlign="right"
191
+ >
192
+ {value}
193
+ </AtomicText>
194
+ </View>
195
+ ) : null}
196
+ </View>
125
197
  );
126
198
  };
127
199
 
128
200
  const getStyles = (tokens: DesignTokens) =>
129
201
  StyleSheet.create({
130
202
  listItem: {
131
- paddingVertical: tokens.spacing.sm,
132
- paddingHorizontal: tokens.spacing.md,
133
- minHeight: 64,
203
+ flexDirection: 'row',
204
+ alignItems: 'center',
205
+ paddingVertical: tokens.spacing.md,
206
+ paddingHorizontal: tokens.spacing.lg,
207
+ minHeight: 72,
208
+ },
209
+ disabled: {
210
+ opacity: 0.5,
134
211
  },
135
212
  leftContainer: {
136
213
  marginRight: tokens.spacing.md,
137
214
  justifyContent: 'center',
138
215
  },
139
216
  iconContainer: {
140
- width: 44,
141
- height: 44,
142
- borderRadius: 22,
217
+ width: 48,
218
+ height: 48,
219
+ borderRadius: 24,
143
220
  alignItems: 'center',
144
221
  justifyContent: 'center',
145
222
  overflow: 'hidden',
146
223
  borderWidth: 1,
147
- borderColor: `${tokens.colors.primary}20`,
224
+ borderColor: `${tokens.colors.primary}15`,
225
+ },
226
+ contentContainer: {
227
+ flex: 1,
228
+ justifyContent: 'center',
229
+ minWidth: 0,
148
230
  },
149
231
  title: {
150
232
  fontSize: tokens.typography.bodyLarge.fontSize,
151
233
  fontWeight: '600',
152
- color: tokens.colors.textPrimary,
153
234
  flexShrink: 1,
235
+ lineHeight: tokens.typography.bodyLarge.fontSize * 1.4,
154
236
  },
155
237
  description: {
156
238
  fontSize: tokens.typography.bodySmall.fontSize,
157
- color: tokens.colors.textSecondary,
158
- marginTop: tokens.spacing.xs,
159
- opacity: 0.8,
239
+ marginTop: tokens.spacing.xs / 2,
240
+ opacity: 0.7,
160
241
  flexShrink: 1,
242
+ lineHeight: tokens.typography.bodySmall.fontSize * 1.4,
161
243
  },
162
244
  rightContainer: {
163
245
  justifyContent: 'center',
164
246
  alignItems: 'flex-end',
247
+ marginLeft: tokens.spacing.md,
165
248
  maxWidth: '50%',
166
249
  flexShrink: 0,
167
250
  },
168
251
  value: {
169
252
  fontWeight: '500',
170
- textAlign: 'right',
253
+ lineHeight: tokens.typography.bodyMedium.fontSize * 1.4,
171
254
  },
172
255
  chevron: {
173
- opacity: 0.6,
256
+ opacity: 0.5,
174
257
  },
175
258
  });
176
259
 
@@ -11,18 +11,18 @@
11
11
 
12
12
  import React from 'react';
13
13
  import { View, StyleSheet } from 'react-native';
14
- import { List } from 'react-native-paper';
15
14
 
16
15
  import { useNavigation } from '@react-navigation/native';
17
- import { useTheme, useAppDesignTokens, type DesignTokens } from '@umituz/react-native-design-system-theme';
18
- import { AtomicText, ScreenLayout } from '@umituz/react-native-design-system';
16
+ import { useDesignSystemTheme, useAppDesignTokens, type DesignTokens } from '@umituz/react-native-design-system-theme';
17
+ import { AtomicText, ScreenLayout, SectionHeader, SectionContainer } from '@umituz/react-native-design-system';
19
18
  import { useLocalization, getLanguageByCode } from '@umituz/react-native-localization';
20
19
  import { SettingItem } from '../components/SettingItem';
21
20
 
22
21
  export const AppearanceScreen: React.FC = () => {
23
22
  const { t, currentLanguage } = useLocalization();
24
23
  const navigation = useNavigation();
25
- const { themeMode, toggleTheme } = useTheme();
24
+ // Only read themeMode, no toggle logic here - theme logic belongs in theme package
25
+ const { themeMode } = useDesignSystemTheme();
26
26
  const tokens = useAppDesignTokens();
27
27
  const styles = getStyles(tokens);
28
28
 
@@ -35,7 +35,13 @@ export const AppearanceScreen: React.FC = () => {
35
35
  };
36
36
 
37
37
  const handleThemeToggle = () => {
38
- toggleTheme();
38
+ // Theme toggle logic should be handled by app, not this package
39
+ // This screen only displays current theme mode
40
+ // App should provide toggle functionality via navigation params or event emitter
41
+ // For now, just navigate back - app can handle toggle in its own theme management
42
+ if (navigation.canGoBack()) {
43
+ navigation.goBack();
44
+ }
39
45
  };
40
46
 
41
47
  return (
@@ -51,8 +57,8 @@ export const AppearanceScreen: React.FC = () => {
51
57
  </View>
52
58
 
53
59
  {/* Language Section */}
54
- <List.Section>
55
- <List.Subheader style={{ color: tokens.colors.textSecondary }}>{t('settings.language')}</List.Subheader>
60
+ <SectionContainer>
61
+ <SectionHeader>{t('settings.language')}</SectionHeader>
56
62
  <SettingItem
57
63
  icon="Languages"
58
64
  iconGradient={((tokens.colors as any).settingGradients?.language as unknown as string[]) || [tokens.colors.primary, tokens.colors.secondary]}
@@ -61,11 +67,11 @@ export const AppearanceScreen: React.FC = () => {
61
67
  onPress={handleLanguagePress}
62
68
  testID="language-button"
63
69
  />
64
- </List.Section>
70
+ </SectionContainer>
65
71
 
66
72
  {/* Theme Section */}
67
- <List.Section>
68
- <List.Subheader style={{ color: tokens.colors.textSecondary }}>{t('settings.appearance.darkMode')}</List.Subheader>
73
+ <SectionContainer>
74
+ <SectionHeader>{t('settings.appearance.darkMode')}</SectionHeader>
69
75
  <SettingItem
70
76
  icon={themeMode === 'dark' ? 'Moon' : 'Sun'}
71
77
  iconGradient={
@@ -78,7 +84,7 @@ export const AppearanceScreen: React.FC = () => {
78
84
  onPress={handleThemeToggle}
79
85
  testID="theme-button"
80
86
  />
81
- </List.Section>
87
+ </SectionContainer>
82
88
  </ScreenLayout>
83
89
  );
84
90
  };
@@ -12,13 +12,12 @@
12
12
  */
13
13
 
14
14
  import React, { useMemo } from 'react';
15
- import { List, Divider } from 'react-native-paper';
16
15
  import { DeviceEventEmitter, Alert, View, TouchableOpacity, StyleSheet } from 'react-native';
17
16
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
18
17
 
19
18
  import { useNavigation, CommonActions } from '@react-navigation/native';
20
- import { useTheme, useAppDesignTokens } from '@umituz/react-native-design-system-theme';
21
- import { ScreenLayout, AtomicIcon, AtomicText } from '@umituz/react-native-design-system';
19
+ import { useDesignSystemTheme, useAppDesignTokens } from '@umituz/react-native-design-system-theme';
20
+ import { ScreenLayout, AtomicIcon, AtomicText, SectionHeader, SectionContainer, AtomicDivider } from '@umituz/react-native-design-system';
22
21
  import { SettingItem } from '../components/SettingItem';
23
22
  import { getLanguageByCode, useLocalization } from '@umituz/react-native-localization';
24
23
  import { SettingsConfig } from './types';
@@ -87,7 +86,8 @@ export const SettingsScreen: React.FC<SettingsScreenProps> = ({
87
86
  config = {}
88
87
  }) => {
89
88
  const navigation = useNavigation();
90
- const { theme, themeMode } = useTheme();
89
+ // Only read themeMode, no theme logic here - theme logic belongs in theme package
90
+ const { themeMode } = useDesignSystemTheme();
91
91
  const tokens = useAppDesignTokens();
92
92
  const insets = useSafeAreaInsets();
93
93
  const { currentLanguage, t } = useLocalization();
@@ -247,12 +247,12 @@ export const SettingsScreen: React.FC<SettingsScreenProps> = ({
247
247
  <View style={[
248
248
  styles.header,
249
249
  {
250
- borderBottomColor: theme.colors.borderLight,
251
- backgroundColor: theme.colors.surface,
250
+ borderBottomColor: tokens.colors.borderLight,
251
+ backgroundColor: tokens.colors.surface,
252
252
  paddingTop: insets.top,
253
253
  }
254
254
  ]}>
255
- <AtomicText type="headlineLarge" style={{ color: theme.colors.textPrimary, flex: 1 }}>
255
+ <AtomicText type="headlineLarge" style={{ color: tokens.colors.textPrimary, flex: 1 }}>
256
256
  {t('navigation.settings') || 'Settings'}
257
257
  </AtomicText>
258
258
  <TouchableOpacity
@@ -267,84 +267,84 @@ export const SettingsScreen: React.FC<SettingsScreenProps> = ({
267
267
 
268
268
  {/* Appearance Section */}
269
269
  {features.appearance && (
270
- <List.Section style={{ marginBottom: 8 }}>
271
- <List.Subheader style={{ color: theme.colors.textSecondary }}>{t('settings.sections.appearance')}</List.Subheader>
270
+ <SectionContainer>
271
+ <SectionHeader>{t('settings.sections.appearance')}</SectionHeader>
272
272
  <SettingItem
273
273
  icon="Palette"
274
- iconGradient={theme.colors.settingGradients.themeLight as unknown as string[]}
274
+ iconGradient={((tokens.colors as any).settingGradients?.themeLight as unknown as string[]) || [tokens.colors.primary, tokens.colors.secondary]}
275
275
  title={t('settings.appearance.title')}
276
276
  description={t('settings.appearance.themeDescription')}
277
277
  onPress={handleAppearancePress}
278
278
  testID="appearance-button"
279
279
  />
280
- </List.Section>
280
+ </SectionContainer>
281
281
  )}
282
282
 
283
283
  {/* General Section - Notifications */}
284
284
  {features.notifications && (
285
- <List.Section style={{ marginBottom: 8 }}>
286
- <List.Subheader style={{ color: theme.colors.textSecondary }}>{t('settings.sections.general')}</List.Subheader>
285
+ <SectionContainer>
286
+ <SectionHeader>{t('settings.sections.general')}</SectionHeader>
287
287
  <SettingItem
288
288
  icon="Bell"
289
- iconGradient={theme.colors.settingGradients.notifications as unknown as string[]}
289
+ iconGradient={((tokens.colors as any).settingGradients?.notifications as unknown as string[]) || [tokens.colors.primary, tokens.colors.secondary]}
290
290
  title={t('settings.notifications.title')}
291
291
  description={t('settings.notifications.description')}
292
292
  onPress={handleNotificationsPress}
293
293
  testID="notifications-button"
294
294
  />
295
- </List.Section>
295
+ </SectionContainer>
296
296
  )}
297
297
 
298
298
  {/* Development/Test: Show Onboarding */}
299
299
  {__DEV__ && useOnboardingStore && (
300
- <List.Section style={{ marginBottom: 8 }}>
301
- <List.Subheader style={{ color: theme.colors.textSecondary }}>Development</List.Subheader>
300
+ <SectionContainer>
301
+ <SectionHeader>Development</SectionHeader>
302
302
  <SettingItem
303
303
  icon="Play"
304
- iconGradient={theme.colors.settingGradients.info as unknown as string[]}
304
+ iconGradient={((tokens.colors as any).settingGradients?.info as unknown as string[]) || [tokens.colors.primary, tokens.colors.secondary]}
305
305
  title="Show Onboarding (Dev)"
306
306
  description="Navigate to onboarding screen"
307
307
  onPress={handleShowOnboarding}
308
308
  testID="show-onboarding-button"
309
309
  />
310
- </List.Section>
310
+ </SectionContainer>
311
311
  )}
312
312
 
313
313
  {/* About & Legal Section */}
314
314
  {(features.about || features.legal) && (
315
- <List.Section style={{ marginBottom: 8 }}>
316
- <List.Subheader style={{ color: theme.colors.textSecondary }}>{t('settings.sections.about')}</List.Subheader>
315
+ <SectionContainer>
316
+ <SectionHeader>{t('settings.sections.about')}</SectionHeader>
317
317
  {features.about && (
318
318
  <SettingItem
319
319
  icon="Info"
320
- iconGradient={theme.colors.settingGradients.info as unknown as string[]}
320
+ iconGradient={((tokens.colors as any).settingGradients?.info as unknown as string[]) || [tokens.colors.primary, tokens.colors.secondary]}
321
321
  title={t('settings.about.title')}
322
322
  description={t('settings.about.description')}
323
323
  onPress={handleAboutPress}
324
324
  testID="about-button"
325
325
  />
326
326
  )}
327
- {features.about && features.legal && <Divider />}
327
+ {features.about && features.legal && <AtomicDivider />}
328
328
  {features.legal && (
329
329
  <SettingItem
330
330
  icon="FileText"
331
- iconGradient={theme.colors.settingGradients.info as unknown as string[]}
331
+ iconGradient={((tokens.colors as any).settingGradients?.info as unknown as string[]) || [tokens.colors.primary, tokens.colors.secondary]}
332
332
  title={t('settings.legal.title')}
333
333
  description={t('settings.legal.description')}
334
334
  onPress={handleLegalPress}
335
335
  testID="legal-button"
336
336
  />
337
337
  )}
338
- </List.Section>
338
+ </SectionContainer>
339
339
  )}
340
340
 
341
341
  {/* Fallback: Show message if no features are enabled */}
342
342
  {!hasAnyFeatures && (
343
- <List.Section>
344
- <List.Subheader style={{ color: theme.colors.textSecondary }}>
343
+ <SectionContainer>
344
+ <SectionHeader>
345
345
  {t('settings.noOptionsAvailable') || 'No settings available'}
346
- </List.Subheader>
347
- </List.Section>
346
+ </SectionHeader>
347
+ </SectionContainer>
348
348
  )}
349
349
  </ScreenLayout>
350
350
  );