@umituz/react-native-localization 3.5.53 → 3.5.55
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 +17 -5
- package/src/infrastructure/storage/AsyncStorageWrapper.ts +2 -2
- package/src/infrastructure/storage/LanguageInitializer.ts +1 -1
- package/src/infrastructure/storage/LanguageSwitcher.ts +1 -1
- package/src/presentation/screens/LanguageSelectionScreen.tsx +6 -5
- package/src/presentation/components/SearchInput.styles.ts +0 -34
- package/src/presentation/components/SearchInput.tsx +0 -78
- package/src/presentation/components/__tests__/SearchInput.test.tsx +0 -95
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-localization",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.55",
|
|
4
4
|
"description": "Generic localization system for React Native apps with i18n support",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -26,25 +26,37 @@
|
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
28
|
"@react-native-async-storage/async-storage": ">=2.0.0",
|
|
29
|
-
"@umituz/react-native-
|
|
29
|
+
"@umituz/react-native-design-system": "latest",
|
|
30
30
|
"expo-localization": ">=16.0.0",
|
|
31
31
|
"i18next": ">=23.0.0",
|
|
32
32
|
"react": ">=18.2.0",
|
|
33
33
|
"react-i18next": ">=15.0.0",
|
|
34
34
|
"react-native": ">=0.74.0",
|
|
35
|
-
"zustand": ">=4.0.0"
|
|
35
|
+
"zustand": ">=4.0.0",
|
|
36
|
+
"@umituz/react-native-design-system": "latest"
|
|
36
37
|
},
|
|
37
38
|
"devDependencies": {
|
|
38
39
|
"@react-native-async-storage/async-storage": "~2.1.2",
|
|
39
40
|
"@types/react": "~19.1.10",
|
|
40
|
-
"@umituz/react-native-storage": "latest",
|
|
41
41
|
"expo-localization": "~16.0.1",
|
|
42
42
|
"i18next": "^24.2.0",
|
|
43
43
|
"react": "19.1.0",
|
|
44
44
|
"react-i18next": "^15.2.0",
|
|
45
45
|
"react-native": "0.81.5",
|
|
46
46
|
"typescript": "~5.9.2",
|
|
47
|
-
"zustand": "^
|
|
47
|
+
"zustand": "^5.0.0",
|
|
48
|
+
"@umituz/react-native-design-system": "latest",
|
|
49
|
+
"expo-clipboard": "^8.0.0",
|
|
50
|
+
"expo-sharing": "^12.0.0",
|
|
51
|
+
"expo-application": "^5.0.0",
|
|
52
|
+
"expo-device": "^5.0.0",
|
|
53
|
+
"react-native-safe-area-context": "latest",
|
|
54
|
+
"@react-navigation/native": "latest",
|
|
55
|
+
"@react-navigation/stack": "latest",
|
|
56
|
+
"@react-navigation/bottom-tabs": "latest",
|
|
57
|
+
"react-native-gesture-handler": "latest",
|
|
58
|
+
"expo-font": "^12.0.0",
|
|
59
|
+
"expo-file-system": "^17.0.0"
|
|
48
60
|
},
|
|
49
61
|
"publishConfig": {
|
|
50
62
|
"access": "public"
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Storage Wrapper
|
|
3
|
-
* Uses @umituz/react-native-
|
|
3
|
+
* Uses @umituz/react-native-design-system for persistence
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { storageRepository } from '@umituz/react-native-
|
|
6
|
+
import { storageRepository } from '@umituz/react-native-design-system';
|
|
7
7
|
|
|
8
8
|
export const STORAGE_KEYS = {
|
|
9
9
|
LANGUAGE: '@localization:language',
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* - i18n setup
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { storageRepository } from '@umituz/react-native-
|
|
10
|
+
import { storageRepository } from '@umituz/react-native-design-system';
|
|
11
11
|
import i18n from '../config/i18n';
|
|
12
12
|
import { languageRepository } from '../repository/LanguageRepository';
|
|
13
13
|
import { getDeviceLocale } from '../config/languages';
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* - Persistence
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { storageRepository } from '@umituz/react-native-
|
|
9
|
+
import { storageRepository } from '@umituz/react-native-design-system';
|
|
10
10
|
import i18n from '../config/i18n';
|
|
11
11
|
import { languageRepository } from '../repository/LanguageRepository';
|
|
12
12
|
|
|
@@ -7,10 +7,10 @@ import React from 'react';
|
|
|
7
7
|
import { View, 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 { useAppDesignTokens, SearchBar } from '@umituz/react-native-design-system';
|
|
11
12
|
import { useLanguageSelection } from '../../infrastructure/hooks/useLanguageSelection';
|
|
12
13
|
import { LanguageItem } from '../components/LanguageItem';
|
|
13
|
-
import { SearchInput } from '../components/SearchInput';
|
|
14
14
|
import type { Language } from '../../infrastructure/storage/types/Language';
|
|
15
15
|
import { styles } from './LanguageSelectionScreen.styles';
|
|
16
16
|
|
|
@@ -80,11 +80,12 @@ export const LanguageSelectionScreen: React.FC<LanguageSelectionScreenProps> = (
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
return (
|
|
83
|
-
<
|
|
83
|
+
<SearchBar
|
|
84
84
|
value={searchQuery}
|
|
85
|
-
|
|
85
|
+
onChangeText={setSearchQuery}
|
|
86
86
|
placeholder={searchPlaceholder}
|
|
87
|
-
|
|
87
|
+
containerStyle={customStyles?.searchContainer}
|
|
88
|
+
inputStyle={customStyles?.searchInput}
|
|
88
89
|
/>
|
|
89
90
|
);
|
|
90
91
|
};
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Search Input Component Styles
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { StyleSheet } from 'react-native';
|
|
6
|
-
|
|
7
|
-
export const styles = StyleSheet.create({
|
|
8
|
-
searchContainer: {
|
|
9
|
-
flexDirection: 'row',
|
|
10
|
-
alignItems: 'center',
|
|
11
|
-
marginHorizontal: 20,
|
|
12
|
-
marginBottom: 24,
|
|
13
|
-
paddingHorizontal: 16,
|
|
14
|
-
paddingVertical: 12,
|
|
15
|
-
borderRadius: 12,
|
|
16
|
-
borderWidth: 1,
|
|
17
|
-
},
|
|
18
|
-
searchIcon: {
|
|
19
|
-
marginRight: 12,
|
|
20
|
-
fontSize: 16,
|
|
21
|
-
},
|
|
22
|
-
searchInput: {
|
|
23
|
-
flex: 1,
|
|
24
|
-
fontSize: 16,
|
|
25
|
-
padding: 0,
|
|
26
|
-
fontWeight: '500',
|
|
27
|
-
},
|
|
28
|
-
clearButton: {
|
|
29
|
-
padding: 4,
|
|
30
|
-
},
|
|
31
|
-
clearIcon: {
|
|
32
|
-
fontSize: 14,
|
|
33
|
-
},
|
|
34
|
-
});
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Search Input Component
|
|
3
|
-
*
|
|
4
|
-
* Renders search input for language filtering
|
|
5
|
-
* Theme-aware component that adapts to light/dark mode
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import React, { useMemo } from 'react';
|
|
9
|
-
import {
|
|
10
|
-
View,
|
|
11
|
-
TextInput,
|
|
12
|
-
TouchableOpacity,
|
|
13
|
-
Text,
|
|
14
|
-
type StyleProp,
|
|
15
|
-
type ViewStyle,
|
|
16
|
-
type TextStyle,
|
|
17
|
-
} from 'react-native';
|
|
18
|
-
// @ts-ignore - Optional peer dependency
|
|
19
|
-
import { useAppDesignTokens } from '@umituz/react-native-design-system';
|
|
20
|
-
import { styles } from './SearchInput.styles';
|
|
21
|
-
|
|
22
|
-
interface SearchInputProps {
|
|
23
|
-
value: string;
|
|
24
|
-
onChange: (value: string) => void;
|
|
25
|
-
placeholder: string;
|
|
26
|
-
customStyles?: {
|
|
27
|
-
searchContainer?: StyleProp<ViewStyle>;
|
|
28
|
-
searchInput?: StyleProp<TextStyle>;
|
|
29
|
-
searchIcon?: StyleProp<TextStyle>;
|
|
30
|
-
clearButton?: StyleProp<ViewStyle>;
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export const SearchInput: React.FC<SearchInputProps> = ({
|
|
35
|
-
value,
|
|
36
|
-
onChange,
|
|
37
|
-
placeholder,
|
|
38
|
-
customStyles,
|
|
39
|
-
}) => {
|
|
40
|
-
const tokens = useAppDesignTokens();
|
|
41
|
-
|
|
42
|
-
const themedStyles = useMemo(() => ({
|
|
43
|
-
searchContainer: {
|
|
44
|
-
backgroundColor: tokens.colors.backgroundSecondary,
|
|
45
|
-
borderColor: tokens.colors.border,
|
|
46
|
-
} as ViewStyle,
|
|
47
|
-
searchInput: {
|
|
48
|
-
color: tokens.colors.textPrimary,
|
|
49
|
-
} as TextStyle,
|
|
50
|
-
clearIcon: {
|
|
51
|
-
color: tokens.colors.textSecondary,
|
|
52
|
-
} as TextStyle,
|
|
53
|
-
}), [tokens]);
|
|
54
|
-
|
|
55
|
-
return (
|
|
56
|
-
<View style={[styles.searchContainer, themedStyles.searchContainer, customStyles?.searchContainer]}>
|
|
57
|
-
<Text style={[styles.searchIcon, customStyles?.searchIcon]}>🔍</Text>
|
|
58
|
-
<TextInput
|
|
59
|
-
style={[styles.searchInput, themedStyles.searchInput, customStyles?.searchInput]}
|
|
60
|
-
placeholder={placeholder}
|
|
61
|
-
placeholderTextColor={tokens.colors.textTertiary}
|
|
62
|
-
value={value}
|
|
63
|
-
onChangeText={onChange}
|
|
64
|
-
autoCapitalize="none"
|
|
65
|
-
autoCorrect={false}
|
|
66
|
-
/>
|
|
67
|
-
{value.length > 0 && (
|
|
68
|
-
<TouchableOpacity
|
|
69
|
-
onPress={() => onChange('')}
|
|
70
|
-
style={[styles.clearButton, customStyles?.clearButton]}
|
|
71
|
-
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
|
|
72
|
-
>
|
|
73
|
-
<Text style={[styles.clearIcon, themedStyles.clearIcon, customStyles?.searchIcon]}>✕</Text>
|
|
74
|
-
</TouchableOpacity>
|
|
75
|
-
)}
|
|
76
|
-
</View>
|
|
77
|
-
);
|
|
78
|
-
};
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Search Input Component Tests
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import React from 'react';
|
|
6
|
-
import { render, fireEvent } from '@testing-library/react-native';
|
|
7
|
-
import { SearchInput } from '../SearchInput';
|
|
8
|
-
|
|
9
|
-
describe('SearchInput', () => {
|
|
10
|
-
const mockOnChange = jest.fn();
|
|
11
|
-
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
mockOnChange.mockClear();
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it('should render with placeholder', () => {
|
|
17
|
-
const { getByPlaceholderText } = render(
|
|
18
|
-
<SearchInput
|
|
19
|
-
value=""
|
|
20
|
-
onChange={mockOnChange}
|
|
21
|
-
placeholder="Search languages..."
|
|
22
|
-
/>
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
expect(getByPlaceholderText('Search languages...')).toBeTruthy();
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('should call onChange when text changes', () => {
|
|
29
|
-
const { getByPlaceholderText } = render(
|
|
30
|
-
<SearchInput
|
|
31
|
-
value=""
|
|
32
|
-
onChange={mockOnChange}
|
|
33
|
-
placeholder="Search languages..."
|
|
34
|
-
/>
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
fireEvent.changeText(getByPlaceholderText('Search languages...'), 'test');
|
|
38
|
-
expect(mockOnChange).toHaveBeenCalledWith('test');
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('should show clear button when text is present', () => {
|
|
42
|
-
const { getByText } = render(
|
|
43
|
-
<SearchInput
|
|
44
|
-
value="test"
|
|
45
|
-
onChange={mockOnChange}
|
|
46
|
-
placeholder="Search languages..."
|
|
47
|
-
/>
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
expect(getByText('✕')).toBeTruthy();
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('should not show clear button when text is empty', () => {
|
|
54
|
-
const { queryByText } = render(
|
|
55
|
-
<SearchInput
|
|
56
|
-
value=""
|
|
57
|
-
onChange={mockOnChange}
|
|
58
|
-
placeholder="Search languages..."
|
|
59
|
-
/>
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
expect(queryByText('✕')).toBeFalsy();
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('should clear text when clear button is pressed', () => {
|
|
66
|
-
const { getByText } = render(
|
|
67
|
-
<SearchInput
|
|
68
|
-
value="test"
|
|
69
|
-
onChange={mockOnChange}
|
|
70
|
-
placeholder="Search languages..."
|
|
71
|
-
/>
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
fireEvent.press(getByText('✕'));
|
|
75
|
-
expect(mockOnChange).toHaveBeenCalledWith('');
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('should apply custom styles', () => {
|
|
79
|
-
const customStyles = {
|
|
80
|
-
searchContainer: { backgroundColor: 'red' },
|
|
81
|
-
searchInput: { fontSize: 20 },
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
const { getByPlaceholderText } = render(
|
|
85
|
-
<SearchInput
|
|
86
|
-
value=""
|
|
87
|
-
onChange={mockOnChange}
|
|
88
|
-
placeholder="Search languages..."
|
|
89
|
-
customStyles={customStyles}
|
|
90
|
-
/>
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
expect(getByPlaceholderText('Search languages...')).toBeTruthy();
|
|
94
|
-
});
|
|
95
|
-
});
|