@umituz/react-native-localization 3.5.57 → 3.5.58
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 +1 -1
- package/src/infrastructure/components/LanguageSwitcher.tsx +31 -38
- package/src/presentation/components/LanguageItem.styles.ts +4 -10
- package/src/presentation/components/LanguageItem.tsx +23 -15
- package/src/presentation/screens/LanguageSelectionScreen.styles.ts +4 -3
- package/src/presentation/screens/LanguageSelectionScreen.tsx +44 -11
package/package.json
CHANGED
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import React, { useMemo } from 'react';
|
|
7
|
-
import { TouchableOpacity,
|
|
7
|
+
import { TouchableOpacity, View } from 'react-native';
|
|
8
|
+
// @ts-ignore - Optional peer dependency
|
|
9
|
+
import { useAppDesignTokens, AtomicText } from '@umituz/react-native-design-system';
|
|
8
10
|
import { useLanguageSwitcher } from './useLanguageSwitcher';
|
|
9
11
|
import { styles, DEFAULT_CONFIG_VALUES } from './LanguageSwitcher.styles';
|
|
10
12
|
|
|
@@ -21,41 +23,6 @@ export interface LanguageSwitcherProps {
|
|
|
21
23
|
accessibilityLabel?: string;
|
|
22
24
|
}
|
|
23
25
|
|
|
24
|
-
const renderContent = (
|
|
25
|
-
showFlag: boolean,
|
|
26
|
-
showName: boolean,
|
|
27
|
-
flag: string | undefined,
|
|
28
|
-
nativeName: string,
|
|
29
|
-
color: string | undefined,
|
|
30
|
-
iconStyle: any,
|
|
31
|
-
textStyle: any
|
|
32
|
-
) => {
|
|
33
|
-
if (showFlag && showName) {
|
|
34
|
-
return (
|
|
35
|
-
<>
|
|
36
|
-
<Text style={[styles.flag, iconStyle]}>{flag}</Text>
|
|
37
|
-
<Text style={[styles.languageName, { color }, textStyle]}>
|
|
38
|
-
{nativeName}
|
|
39
|
-
</Text>
|
|
40
|
-
</>
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (showFlag) {
|
|
45
|
-
return <Text style={[styles.flag, iconStyle]}>{flag}</Text>;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (showName) {
|
|
49
|
-
return (
|
|
50
|
-
<Text style={[styles.languageName, { color }, textStyle]}>
|
|
51
|
-
{nativeName}
|
|
52
|
-
</Text>
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return <Text style={[styles.icon, { color }, iconStyle]}>🌐</Text>;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
26
|
export const LanguageSwitcher: React.FC<LanguageSwitcherProps> = ({
|
|
60
27
|
showName = false,
|
|
61
28
|
showFlag = true,
|
|
@@ -68,6 +35,7 @@ export const LanguageSwitcher: React.FC<LanguageSwitcherProps> = ({
|
|
|
68
35
|
disabled = false,
|
|
69
36
|
accessibilityLabel,
|
|
70
37
|
}) => {
|
|
38
|
+
const tokens = useAppDesignTokens();
|
|
71
39
|
const { currentLang, handlePress } = useLanguageSwitcher({ onPress, disabled });
|
|
72
40
|
|
|
73
41
|
const accessibilityProps = useMemo(() => ({
|
|
@@ -77,9 +45,16 @@ export const LanguageSwitcher: React.FC<LanguageSwitcherProps> = ({
|
|
|
77
45
|
accessible: true,
|
|
78
46
|
}), [accessibilityLabel, currentLang.nativeName, disabled]);
|
|
79
47
|
|
|
48
|
+
const textColor = color || tokens.colors.textPrimary;
|
|
49
|
+
|
|
80
50
|
return (
|
|
81
51
|
<TouchableOpacity
|
|
82
|
-
style={[
|
|
52
|
+
style={[
|
|
53
|
+
styles.container,
|
|
54
|
+
{ gap: tokens.spacing.xs },
|
|
55
|
+
style,
|
|
56
|
+
disabled && styles.disabled
|
|
57
|
+
]}
|
|
83
58
|
onPress={handlePress}
|
|
84
59
|
activeOpacity={disabled ? 1 : DEFAULT_CONFIG_VALUES.activeOpacity}
|
|
85
60
|
hitSlop={DEFAULT_CONFIG_VALUES.hitSlop}
|
|
@@ -87,9 +62,27 @@ export const LanguageSwitcher: React.FC<LanguageSwitcherProps> = ({
|
|
|
87
62
|
disabled={disabled}
|
|
88
63
|
{...accessibilityProps}
|
|
89
64
|
>
|
|
90
|
-
{
|
|
65
|
+
{showFlag && (
|
|
66
|
+
<AtomicText style={[styles.flag, iconStyle]}>
|
|
67
|
+
{currentLang.flag || '🌐'}
|
|
68
|
+
</AtomicText>
|
|
69
|
+
)}
|
|
70
|
+
{showName && (
|
|
71
|
+
<AtomicText
|
|
72
|
+
type="bodySmall"
|
|
73
|
+
style={[styles.languageName, { color: textColor, fontWeight: '600' }, textStyle]}
|
|
74
|
+
>
|
|
75
|
+
{currentLang.nativeName}
|
|
76
|
+
</AtomicText>
|
|
77
|
+
)}
|
|
78
|
+
{!showFlag && !showName && (
|
|
79
|
+
<AtomicText style={[styles.icon, { color: textColor }, iconStyle]}>
|
|
80
|
+
🌐
|
|
81
|
+
</AtomicText>
|
|
82
|
+
)}
|
|
91
83
|
</TouchableOpacity>
|
|
92
84
|
);
|
|
93
85
|
};
|
|
94
86
|
|
|
95
87
|
export default LanguageSwitcher;
|
|
88
|
+
|
|
@@ -9,10 +9,7 @@ export const styles = StyleSheet.create({
|
|
|
9
9
|
flexDirection: 'row',
|
|
10
10
|
alignItems: 'center',
|
|
11
11
|
justifyContent: 'space-between',
|
|
12
|
-
padding: 16,
|
|
13
|
-
borderRadius: 12,
|
|
14
12
|
borderWidth: 1,
|
|
15
|
-
marginBottom: 8,
|
|
16
13
|
},
|
|
17
14
|
selectedLanguageItem: {
|
|
18
15
|
borderWidth: 2,
|
|
@@ -29,18 +26,15 @@ export const styles = StyleSheet.create({
|
|
|
29
26
|
languageText: {
|
|
30
27
|
flex: 1,
|
|
31
28
|
flexShrink: 1,
|
|
32
|
-
marginRight: 12,
|
|
33
29
|
},
|
|
34
30
|
nativeName: {
|
|
35
|
-
|
|
36
|
-
fontWeight: '600',
|
|
37
|
-
marginBottom: 4,
|
|
31
|
+
// Styling moved to themedStyles in component for token support
|
|
38
32
|
},
|
|
39
33
|
languageName: {
|
|
40
|
-
|
|
34
|
+
// Styling moved to themedStyles in component for token support
|
|
41
35
|
},
|
|
42
36
|
checkIcon: {
|
|
43
|
-
|
|
44
|
-
fontWeight: 'bold',
|
|
37
|
+
// Replaced by AtomicIcon
|
|
45
38
|
},
|
|
46
39
|
});
|
|
40
|
+
|
|
@@ -9,13 +9,12 @@ import React, { useMemo } from 'react';
|
|
|
9
9
|
import {
|
|
10
10
|
View,
|
|
11
11
|
TouchableOpacity,
|
|
12
|
-
Text,
|
|
13
12
|
type StyleProp,
|
|
14
13
|
type ViewStyle,
|
|
15
14
|
type TextStyle,
|
|
16
15
|
} from 'react-native';
|
|
17
16
|
// @ts-ignore - Optional peer dependency
|
|
18
|
-
import { useAppDesignTokens } from '@umituz/react-native-design-system';
|
|
17
|
+
import { useAppDesignTokens, AtomicText, AtomicIcon } from '@umituz/react-native-design-system';
|
|
19
18
|
import type { Language } from '../../infrastructure/storage/types/Language';
|
|
20
19
|
import { styles } from './LanguageItem.styles';
|
|
21
20
|
|
|
@@ -42,22 +41,24 @@ export const LanguageItem: React.FC<LanguageItemProps> = ({
|
|
|
42
41
|
|
|
43
42
|
const themedStyles = useMemo(() => ({
|
|
44
43
|
languageItem: {
|
|
44
|
+
padding: tokens.spacing.md,
|
|
45
|
+
borderRadius: tokens.borders.radius.md,
|
|
45
46
|
backgroundColor: tokens.colors.backgroundSecondary,
|
|
46
47
|
borderColor: tokens.colors.border,
|
|
48
|
+
marginBottom: tokens.spacing.sm,
|
|
47
49
|
} as ViewStyle,
|
|
48
50
|
selectedLanguageItem: {
|
|
49
51
|
borderColor: tokens.colors.primary,
|
|
50
|
-
backgroundColor: tokens.colors.
|
|
52
|
+
backgroundColor: tokens.colors.primaryContainer,
|
|
53
|
+
borderWidth: 2,
|
|
51
54
|
} as ViewStyle,
|
|
52
55
|
nativeName: {
|
|
53
56
|
color: tokens.colors.textPrimary,
|
|
57
|
+
marginBottom: tokens.spacing.xs / 2,
|
|
54
58
|
} as TextStyle,
|
|
55
59
|
languageName: {
|
|
56
60
|
color: tokens.colors.textSecondary,
|
|
57
61
|
} as TextStyle,
|
|
58
|
-
checkIcon: {
|
|
59
|
-
color: tokens.colors.primary,
|
|
60
|
-
} as TextStyle,
|
|
61
62
|
}), [tokens]);
|
|
62
63
|
|
|
63
64
|
return (
|
|
@@ -66,28 +67,35 @@ export const LanguageItem: React.FC<LanguageItemProps> = ({
|
|
|
66
67
|
style={[
|
|
67
68
|
styles.languageItem,
|
|
68
69
|
themedStyles.languageItem,
|
|
69
|
-
customStyles?.languageItem,
|
|
70
70
|
isSelected ? [styles.selectedLanguageItem, themedStyles.selectedLanguageItem] : undefined,
|
|
71
|
+
customStyles?.languageItem,
|
|
71
72
|
]}
|
|
72
73
|
onPress={() => onSelect(item.code)}
|
|
73
74
|
activeOpacity={0.7}
|
|
74
75
|
>
|
|
75
76
|
<View style={[styles.languageContent, customStyles?.languageContent]}>
|
|
76
|
-
<
|
|
77
|
+
<AtomicText style={[styles.flag, customStyles?.flag]}>
|
|
77
78
|
{item.flag || '🌐'}
|
|
78
|
-
</
|
|
79
|
-
<View style={[styles.languageText, customStyles?.languageText]}>
|
|
80
|
-
<
|
|
79
|
+
</AtomicText>
|
|
80
|
+
<View style={[styles.languageText, { gap: tokens.spacing.xs / 2 }, customStyles?.languageText]}>
|
|
81
|
+
<AtomicText
|
|
82
|
+
type="bodyMedium"
|
|
83
|
+
style={[themedStyles.nativeName, { fontWeight: '700' }, customStyles?.nativeName]}
|
|
84
|
+
>
|
|
81
85
|
{item.nativeName}
|
|
82
|
-
</
|
|
83
|
-
<
|
|
86
|
+
</AtomicText>
|
|
87
|
+
<AtomicText
|
|
88
|
+
type="labelMedium"
|
|
89
|
+
style={[themedStyles.languageName, customStyles?.nativeName]}
|
|
90
|
+
>
|
|
84
91
|
{item.name}
|
|
85
|
-
</
|
|
92
|
+
</AtomicText>
|
|
86
93
|
</View>
|
|
87
94
|
</View>
|
|
88
95
|
{isSelected && (
|
|
89
|
-
<
|
|
96
|
+
<AtomicIcon name="checkmark" size="sm" color="primary" />
|
|
90
97
|
)}
|
|
91
98
|
</TouchableOpacity>
|
|
92
99
|
);
|
|
93
100
|
};
|
|
101
|
+
|
|
@@ -6,10 +6,11 @@ import { StyleSheet } from 'react-native';
|
|
|
6
6
|
|
|
7
7
|
export const styles = StyleSheet.create({
|
|
8
8
|
container: {
|
|
9
|
-
|
|
9
|
+
// Styling handled by ScreenLayout
|
|
10
10
|
},
|
|
11
11
|
listContent: {
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
// Horizontal padding handled by ScreenLayout contentWrapper
|
|
13
|
+
// Bottom padding handled in component using tokens
|
|
14
14
|
},
|
|
15
15
|
});
|
|
16
|
+
|
|
@@ -4,11 +4,16 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import React from 'react';
|
|
7
|
-
import {
|
|
7
|
+
import { FlatList } from 'react-native';
|
|
8
8
|
// @ts-ignore - Optional peer dependency
|
|
9
9
|
import { useNavigation } from '@react-navigation/native';
|
|
10
10
|
// @ts-ignore - Optional peer dependency
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
useAppDesignTokens,
|
|
13
|
+
SearchBar,
|
|
14
|
+
ScreenLayout,
|
|
15
|
+
NavigationHeader
|
|
16
|
+
} from '@umituz/react-native-design-system';
|
|
12
17
|
import { useLanguageSelection } from '../../infrastructure/hooks/useLanguageSelection';
|
|
13
18
|
import { LanguageItem } from '../components/LanguageItem';
|
|
14
19
|
import type { Language } from '../../infrastructure/storage/types/Language';
|
|
@@ -17,7 +22,8 @@ import { styles } from './LanguageSelectionScreen.styles';
|
|
|
17
22
|
interface LanguageSelectionScreenProps {
|
|
18
23
|
renderLanguageItem?: (item: Language, isSelected: boolean, onSelect: (code: string) => void) => React.ReactNode;
|
|
19
24
|
renderSearchInput?: (value: string, onChange: (value: string) => void, placeholder: string) => React.ReactNode;
|
|
20
|
-
|
|
25
|
+
headerTitle?: string;
|
|
26
|
+
onBackPress?: () => void;
|
|
21
27
|
styles?: {
|
|
22
28
|
container?: any;
|
|
23
29
|
searchContainer?: any;
|
|
@@ -38,7 +44,8 @@ interface LanguageSelectionScreenProps {
|
|
|
38
44
|
export const LanguageSelectionScreen: React.FC<LanguageSelectionScreenProps> = ({
|
|
39
45
|
renderLanguageItem,
|
|
40
46
|
renderSearchInput,
|
|
41
|
-
|
|
47
|
+
headerTitle,
|
|
48
|
+
onBackPress,
|
|
42
49
|
styles: customStyles,
|
|
43
50
|
searchPlaceholder = "settings.languageSelection.searchPlaceholder",
|
|
44
51
|
testID = 'language-selection-screen',
|
|
@@ -84,27 +91,53 @@ export const LanguageSelectionScreen: React.FC<LanguageSelectionScreenProps> = (
|
|
|
84
91
|
value={searchQuery}
|
|
85
92
|
onChangeText={setSearchQuery}
|
|
86
93
|
placeholder={searchPlaceholder}
|
|
87
|
-
containerStyle={
|
|
94
|
+
containerStyle={[
|
|
95
|
+
{ marginBottom: tokens.spacing.md },
|
|
96
|
+
customStyles?.searchContainer
|
|
97
|
+
]}
|
|
88
98
|
inputStyle={customStyles?.searchInput}
|
|
89
99
|
/>
|
|
90
100
|
);
|
|
91
101
|
};
|
|
92
102
|
|
|
93
|
-
const
|
|
94
|
-
|
|
103
|
+
const handleBack = () => {
|
|
104
|
+
if (onBackPress) {
|
|
105
|
+
onBackPress();
|
|
106
|
+
} else {
|
|
107
|
+
navigation.goBack();
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<ScreenLayout
|
|
113
|
+
testID={testID}
|
|
114
|
+
scrollable={false}
|
|
115
|
+
edges={['top', 'bottom', 'left', 'right']}
|
|
116
|
+
backgroundColor={tokens.colors.backgroundPrimary}
|
|
117
|
+
header={
|
|
118
|
+
<NavigationHeader
|
|
119
|
+
title={headerTitle || ""}
|
|
120
|
+
onBackPress={handleBack}
|
|
121
|
+
/>
|
|
122
|
+
}
|
|
123
|
+
containerStyle={customStyles?.container}
|
|
124
|
+
>
|
|
95
125
|
{renderSearchComponent()}
|
|
96
126
|
<FlatList
|
|
97
127
|
data={filteredLanguages}
|
|
98
128
|
renderItem={renderItem}
|
|
99
129
|
keyExtractor={item => item.code}
|
|
100
|
-
contentContainerStyle={[
|
|
130
|
+
contentContainerStyle={[
|
|
131
|
+
styles.listContent,
|
|
132
|
+
{ paddingBottom: tokens.spacing.xl },
|
|
133
|
+
customStyles?.listContent
|
|
134
|
+
]}
|
|
101
135
|
showsVerticalScrollIndicator={false}
|
|
102
136
|
keyboardShouldPersistTaps="handled"
|
|
103
137
|
/>
|
|
104
|
-
</
|
|
138
|
+
</ScreenLayout>
|
|
105
139
|
);
|
|
106
|
-
|
|
107
|
-
return Container ? <Container>{content}</Container> : content;
|
|
108
140
|
};
|
|
109
141
|
|
|
110
142
|
export default LanguageSelectionScreen;
|
|
143
|
+
|