@sudobility/building_blocks_rn 0.0.30 → 0.0.32
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/dist/src/components/pages/AppTextScreen.js +8 -21
- package/dist/src/components/settings/LanguagePicker.js +5 -6
- package/dist/src/components/settings/SettingsListScreen.js +4 -5
- package/dist/src/hooks/index.d.ts +1 -0
- package/dist/src/hooks/index.js +1 -0
- package/dist/src/hooks/useRTL.d.ts +12 -0
- package/dist/src/hooks/useRTL.js +24 -0
- package/package.json +1 -1
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { View, Text, ScrollView } from 'react-native';
|
|
3
3
|
import { createThemedStyles } from '../../utils/styles';
|
|
4
|
+
import { useRTLTextStyle } from '../../hooks/useRTL';
|
|
4
5
|
function TextSectionView({ section, level = 2, }) {
|
|
5
6
|
const styles = useStyles();
|
|
6
|
-
|
|
7
|
+
const rtlText = useRTLTextStyle();
|
|
8
|
+
return (_jsxs(View, { style: styles.section, children: [_jsx(Text, { style: [
|
|
9
|
+
level === 2 ? styles.sectionTitle : styles.subsectionTitle,
|
|
10
|
+
rtlText,
|
|
11
|
+
], children: section.title }), section.content && (_jsx(Text, { style: [styles.sectionContent, rtlText], children: section.content })), section.items && section.items.length > 0 && (_jsx(View, { style: styles.list, children: section.items.map((item, index) => (_jsxs(View, { style: styles.listItem, children: [_jsx(Text, { style: styles.bullet, children: '\u2022' }), _jsx(Text, { style: [styles.listItemText, rtlText], children: item })] }, index))) })), section.subsections?.map((sub, index) => (_jsx(TextSectionView, { section: sub, level: 3 }, index)))] }));
|
|
7
12
|
}
|
|
8
13
|
export function AppTextScreen({ text, lastUpdatedDate, ScreenWrapper, style, }) {
|
|
9
14
|
const styles = useStyles();
|
|
15
|
+
const rtlText = useRTLTextStyle();
|
|
10
16
|
const lastUpdated = text.lastUpdated
|
|
11
17
|
? text.lastUpdated.replace('{{date}}', lastUpdatedDate ?? '')
|
|
12
18
|
: lastUpdatedDate
|
|
13
19
|
? `Last updated: ${lastUpdatedDate}`
|
|
14
20
|
: undefined;
|
|
15
|
-
const content = (_jsxs(View, { style: [styles.container, style], children: [_jsx(Text, { style: styles.title, children: text.title }), lastUpdated && _jsx(Text, { style: styles.lastUpdated, children: lastUpdated }), text.sections.map((section, index) => (_jsx(TextSectionView, { section: section }, index))), text.contact && (_jsxs(View, { style: styles.contactSection, children: [_jsx(Text, { style: styles.sectionTitle, children: text.contact.title }), _jsx(Text, { style: styles.sectionContent, children: text.contact.description }), _jsx(Text, { style: styles.contactInfo, children: text.contact.info }), text.contact.gdprNotice && (_jsx(Text, { style: styles.gdprNotice, children: text.contact.gdprNotice }))] }))] }));
|
|
21
|
+
const content = (_jsxs(View, { style: [styles.container, style], children: [_jsx(Text, { style: [styles.title, rtlText], children: text.title }), lastUpdated && (_jsx(Text, { style: [styles.lastUpdated, rtlText], children: lastUpdated })), text.sections.map((section, index) => (_jsx(TextSectionView, { section: section }, index))), text.contact && (_jsxs(View, { style: styles.contactSection, children: [_jsx(Text, { style: [styles.sectionTitle, rtlText], children: text.contact.title }), _jsx(Text, { style: [styles.sectionContent, rtlText], children: text.contact.description }), _jsx(Text, { style: [styles.contactInfo, rtlText], children: text.contact.info }), text.contact.gdprNotice && (_jsx(Text, { style: [styles.gdprNotice, rtlText], children: text.contact.gdprNotice }))] }))] }));
|
|
16
22
|
if (ScreenWrapper) {
|
|
17
23
|
return _jsx(ScreenWrapper, { children: content });
|
|
18
24
|
}
|
|
@@ -30,52 +36,39 @@ const useStyles = createThemedStyles(colors => ({
|
|
|
30
36
|
fontWeight: '700',
|
|
31
37
|
color: colors.text,
|
|
32
38
|
marginBottom: 8,
|
|
33
|
-
alignSelf: 'flex-start',
|
|
34
|
-
textAlign: 'auto',
|
|
35
39
|
},
|
|
36
40
|
lastUpdated: {
|
|
37
41
|
fontSize: 14,
|
|
38
42
|
color: colors.textMuted,
|
|
39
43
|
marginBottom: 24,
|
|
40
|
-
alignSelf: 'flex-start',
|
|
41
|
-
textAlign: 'auto',
|
|
42
44
|
},
|
|
43
45
|
section: {
|
|
44
46
|
marginBottom: 24,
|
|
45
|
-
alignItems: 'flex-start',
|
|
46
47
|
},
|
|
47
48
|
sectionTitle: {
|
|
48
49
|
fontSize: 20,
|
|
49
50
|
fontWeight: '600',
|
|
50
51
|
color: colors.text,
|
|
51
52
|
marginBottom: 8,
|
|
52
|
-
alignSelf: 'flex-start',
|
|
53
|
-
textAlign: 'auto',
|
|
54
53
|
},
|
|
55
54
|
subsectionTitle: {
|
|
56
55
|
fontSize: 17,
|
|
57
56
|
fontWeight: '600',
|
|
58
57
|
color: colors.text,
|
|
59
58
|
marginBottom: 6,
|
|
60
|
-
alignSelf: 'flex-start',
|
|
61
|
-
textAlign: 'auto',
|
|
62
59
|
},
|
|
63
60
|
sectionContent: {
|
|
64
61
|
fontSize: 15,
|
|
65
62
|
lineHeight: 22,
|
|
66
63
|
color: colors.textSecondary,
|
|
67
|
-
alignSelf: 'flex-start',
|
|
68
|
-
textAlign: 'auto',
|
|
69
64
|
},
|
|
70
65
|
list: {
|
|
71
66
|
marginTop: 8,
|
|
72
67
|
gap: 6,
|
|
73
|
-
alignSelf: 'stretch',
|
|
74
68
|
},
|
|
75
69
|
listItem: {
|
|
76
70
|
flexDirection: 'row',
|
|
77
71
|
paddingStart: 8,
|
|
78
|
-
alignSelf: 'flex-start',
|
|
79
72
|
},
|
|
80
73
|
bullet: {
|
|
81
74
|
fontSize: 15,
|
|
@@ -88,28 +81,22 @@ const useStyles = createThemedStyles(colors => ({
|
|
|
88
81
|
lineHeight: 22,
|
|
89
82
|
color: colors.textSecondary,
|
|
90
83
|
flex: 1,
|
|
91
|
-
textAlign: 'auto',
|
|
92
84
|
},
|
|
93
85
|
contactSection: {
|
|
94
86
|
marginTop: 16,
|
|
95
87
|
padding: 16,
|
|
96
88
|
backgroundColor: colors.surfaceSecondary,
|
|
97
89
|
borderRadius: 12,
|
|
98
|
-
alignItems: 'flex-start',
|
|
99
90
|
},
|
|
100
91
|
contactInfo: {
|
|
101
92
|
fontSize: 15,
|
|
102
93
|
color: colors.primary,
|
|
103
94
|
marginTop: 8,
|
|
104
|
-
alignSelf: 'flex-start',
|
|
105
|
-
textAlign: 'auto',
|
|
106
95
|
},
|
|
107
96
|
gdprNotice: {
|
|
108
97
|
fontSize: 13,
|
|
109
98
|
color: colors.textMuted,
|
|
110
99
|
marginTop: 12,
|
|
111
100
|
fontStyle: 'italic',
|
|
112
|
-
alignSelf: 'flex-start',
|
|
113
|
-
textAlign: 'auto',
|
|
114
101
|
},
|
|
115
102
|
}));
|
|
@@ -14,24 +14,26 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
14
14
|
*/
|
|
15
15
|
import { useState } from 'react';
|
|
16
16
|
import { View, Text, Pressable, Modal, FlatList, SafeAreaView, } from 'react-native';
|
|
17
|
+
import { useRTLTextStyle } from '../../hooks/useRTL';
|
|
17
18
|
import { DEFAULT_LANGUAGES } from '../../constants/languages';
|
|
18
19
|
import { createThemedStyles } from '../../utils/styles';
|
|
19
20
|
export function LanguagePicker({ languages = DEFAULT_LANGUAGES, currentLanguage = 'en', onLanguageChange, label, style, }) {
|
|
20
21
|
const styles = useStyles();
|
|
22
|
+
const rtlText = useRTLTextStyle();
|
|
21
23
|
const [modalVisible, setModalVisible] = useState(false);
|
|
22
24
|
const currentLang = languages.find(l => l.code === currentLanguage);
|
|
23
25
|
const handleSelect = (code) => {
|
|
24
26
|
onLanguageChange?.(code);
|
|
25
27
|
setModalVisible(false);
|
|
26
28
|
};
|
|
27
|
-
return (_jsxs(View, { style: style, children: [label && _jsx(Text, { style: styles.label, children: label }), _jsxs(Pressable, { style: styles.trigger, onPress: () => setModalVisible(true), accessibilityRole: 'button', accessibilityLabel: `${label ?? 'Language'}: ${currentLang?.name ?? currentLanguage}. Tap to change.`, children: [_jsx(Text, { style: styles.triggerText, children: currentLang
|
|
29
|
+
return (_jsxs(View, { style: style, children: [label && _jsx(Text, { style: [styles.label, rtlText], children: label }), _jsxs(Pressable, { style: styles.trigger, onPress: () => setModalVisible(true), accessibilityRole: 'button', accessibilityLabel: `${label ?? 'Language'}: ${currentLang?.name ?? currentLanguage}. Tap to change.`, children: [_jsx(Text, { style: [styles.triggerText, rtlText], children: currentLang
|
|
28
30
|
? `${currentLang.flag} ${currentLang.name}`
|
|
29
|
-
: currentLanguage }), _jsx(Text, { style: styles.chevron, children: '\u25BC' })] }), _jsx(Modal, { visible: modalVisible, animationType: 'slide', presentationStyle: 'pageSheet', onRequestClose: () => setModalVisible(false), children: _jsxs(SafeAreaView, { style: styles.modal, children: [_jsxs(View, { style: styles.modalHeader, children: [_jsx(Text, { style: styles.modalTitle, accessibilityRole: 'header', children: label ?? 'Select Language' }), _jsx(Pressable, { onPress: () => setModalVisible(false), accessibilityRole: 'button', accessibilityLabel: 'Done, close language picker', children: _jsx(Text, { style: styles.closeButton, children: "Done" }) })] }), _jsx(FlatList, { data: languages, keyExtractor: item => item.code, renderItem: ({ item }) => (_jsxs(Pressable, { style: [
|
|
31
|
+
: currentLanguage }), _jsx(Text, { style: styles.chevron, children: '\u25BC' })] }), _jsx(Modal, { visible: modalVisible, animationType: 'slide', presentationStyle: 'pageSheet', onRequestClose: () => setModalVisible(false), children: _jsxs(SafeAreaView, { style: styles.modal, children: [_jsxs(View, { style: styles.modalHeader, children: [_jsx(Text, { style: [styles.modalTitle, rtlText], accessibilityRole: 'header', children: label ?? 'Select Language' }), _jsx(Pressable, { onPress: () => setModalVisible(false), accessibilityRole: 'button', accessibilityLabel: 'Done, close language picker', children: _jsx(Text, { style: styles.closeButton, children: "Done" }) })] }), _jsx(FlatList, { data: languages, keyExtractor: item => item.code, renderItem: ({ item }) => (_jsxs(Pressable, { style: [
|
|
30
32
|
styles.languageRow,
|
|
31
33
|
item.code === currentLanguage && styles.languageRowActive,
|
|
32
34
|
], onPress: () => handleSelect(item.code), accessibilityRole: 'radio', accessibilityState: {
|
|
33
35
|
selected: item.code === currentLanguage,
|
|
34
|
-
}, accessibilityLabel: `${item.name}${item.code === currentLanguage ? ', selected' : ''}`, children: [_jsx(Text, { style: styles.flag, children: item.flag }), _jsx(Text, { style: styles.languageName, children: item.name }), item.code === currentLanguage && (_jsx(Text, { style: styles.checkmark, children: '\u2713' }))] })), ItemSeparatorComponent: () => _jsx(View, { style: styles.separator }) })] }) })] }));
|
|
36
|
+
}, accessibilityLabel: `${item.name}${item.code === currentLanguage ? ', selected' : ''}`, children: [_jsx(Text, { style: styles.flag, children: item.flag }), _jsx(Text, { style: [styles.languageName, rtlText], children: item.name }), item.code === currentLanguage && (_jsx(Text, { style: styles.checkmark, children: '\u2713' }))] })), ItemSeparatorComponent: () => _jsx(View, { style: styles.separator }) })] }) })] }));
|
|
35
37
|
}
|
|
36
38
|
const useStyles = createThemedStyles(colors => ({
|
|
37
39
|
label: {
|
|
@@ -39,7 +41,6 @@ const useStyles = createThemedStyles(colors => ({
|
|
|
39
41
|
fontWeight: '500',
|
|
40
42
|
color: colors.textSecondary,
|
|
41
43
|
marginBottom: 8,
|
|
42
|
-
textAlign: 'auto',
|
|
43
44
|
},
|
|
44
45
|
trigger: {
|
|
45
46
|
flexDirection: 'row',
|
|
@@ -56,7 +57,6 @@ const useStyles = createThemedStyles(colors => ({
|
|
|
56
57
|
triggerText: {
|
|
57
58
|
fontSize: 16,
|
|
58
59
|
color: colors.text,
|
|
59
|
-
textAlign: 'auto',
|
|
60
60
|
},
|
|
61
61
|
chevron: {
|
|
62
62
|
fontSize: 10,
|
|
@@ -103,7 +103,6 @@ const useStyles = createThemedStyles(colors => ({
|
|
|
103
103
|
fontSize: 16,
|
|
104
104
|
color: colors.text,
|
|
105
105
|
flex: 1,
|
|
106
|
-
textAlign: 'auto',
|
|
107
106
|
},
|
|
108
107
|
checkmark: {
|
|
109
108
|
fontSize: 18,
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { View, Text, Pressable, ScrollView } from 'react-native';
|
|
3
3
|
import { createThemedStyles } from '../../utils/styles';
|
|
4
|
+
import { useRTLTextStyle } from '../../hooks/useRTL';
|
|
4
5
|
export function SettingsListScreen({ sections, onSectionPress, title = 'Settings', style, onTrack, }) {
|
|
5
6
|
const styles = useStyles();
|
|
7
|
+
const rtlText = useRTLTextStyle();
|
|
6
8
|
const handlePress = (section) => {
|
|
7
9
|
onTrack?.({
|
|
8
10
|
eventType: 'navigation',
|
|
@@ -12,12 +14,12 @@ export function SettingsListScreen({ sections, onSectionPress, title = 'Settings
|
|
|
12
14
|
});
|
|
13
15
|
onSectionPress(section.id);
|
|
14
16
|
};
|
|
15
|
-
return (_jsxs(ScrollView, { style: [styles.container, style], contentContainerStyle: styles.content, children: [_jsx(Text, { style: styles.title, accessibilityRole: 'header', children: title }), _jsx(View, { style: styles.sectionList, accessibilityRole: 'list', children: sections.map((section, index) => {
|
|
17
|
+
return (_jsxs(ScrollView, { style: [styles.container, style], contentContainerStyle: styles.content, children: [_jsx(Text, { style: [styles.title, rtlText], accessibilityRole: 'header', children: title }), _jsx(View, { style: styles.sectionList, accessibilityRole: 'list', children: sections.map((section, index) => {
|
|
16
18
|
const IconComponent = section.icon;
|
|
17
19
|
return (_jsxs(Pressable, { style: [
|
|
18
20
|
styles.sectionRow,
|
|
19
21
|
index < sections.length - 1 && styles.sectionRowBorder,
|
|
20
|
-
], onPress: () => handlePress(section), accessibilityRole: 'button', accessibilityLabel: `${section.label}${section.description ? `, ${section.description}` : ''}`, accessibilityHint: `Opens ${section.label} settings`, children: [IconComponent && (_jsx(View, { style: styles.iconContainer, children: _jsx(IconComponent, { size: 20, color: styles.iconColor.color }) })), _jsxs(View, { style: styles.sectionInfo, children: [_jsx(Text, { style: styles.sectionLabel, children: section.label }), section.description && (_jsx(Text, { style: styles.sectionDescription, children: section.description }))] }), _jsx(Text, { style: styles.chevron, children: '\u203A' })] }, section.id));
|
|
22
|
+
], onPress: () => handlePress(section), accessibilityRole: 'button', accessibilityLabel: `${section.label}${section.description ? `, ${section.description}` : ''}`, accessibilityHint: `Opens ${section.label} settings`, children: [IconComponent && (_jsx(View, { style: styles.iconContainer, children: _jsx(IconComponent, { size: 20, color: styles.iconColor.color }) })), _jsxs(View, { style: styles.sectionInfo, children: [_jsx(Text, { style: [styles.sectionLabel, rtlText], children: section.label }), section.description && (_jsx(Text, { style: [styles.sectionDescription, rtlText], children: section.description }))] }), _jsx(Text, { style: styles.chevron, children: '\u203A' })] }, section.id));
|
|
21
23
|
}) })] }));
|
|
22
24
|
}
|
|
23
25
|
const useStyles = createThemedStyles(colors => ({
|
|
@@ -33,7 +35,6 @@ const useStyles = createThemedStyles(colors => ({
|
|
|
33
35
|
fontWeight: '700',
|
|
34
36
|
color: colors.text,
|
|
35
37
|
marginBottom: 20,
|
|
36
|
-
textAlign: 'auto',
|
|
37
38
|
},
|
|
38
39
|
sectionList: {
|
|
39
40
|
backgroundColor: colors.card,
|
|
@@ -70,13 +71,11 @@ const useStyles = createThemedStyles(colors => ({
|
|
|
70
71
|
fontSize: 16,
|
|
71
72
|
fontWeight: '500',
|
|
72
73
|
color: colors.text,
|
|
73
|
-
textAlign: 'auto',
|
|
74
74
|
},
|
|
75
75
|
sectionDescription: {
|
|
76
76
|
fontSize: 13,
|
|
77
77
|
color: colors.textMuted,
|
|
78
78
|
marginTop: 2,
|
|
79
|
-
textAlign: 'auto',
|
|
80
79
|
},
|
|
81
80
|
chevron: {
|
|
82
81
|
fontSize: 22,
|
package/dist/src/hooks/index.js
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type TextStyle } from 'react-native';
|
|
2
|
+
/**
|
|
3
|
+
* Returns whether the current language is RTL.
|
|
4
|
+
* Checks both I18nManager (native layout) and the current i18n language
|
|
5
|
+
* (covers the session before a restart applies forceRTL).
|
|
6
|
+
*/
|
|
7
|
+
export declare function useIsRTL(): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Returns a text style object that sets the correct text alignment
|
|
10
|
+
* for the current language direction.
|
|
11
|
+
*/
|
|
12
|
+
export declare function useRTLTextStyle(): TextStyle;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { I18nManager } from 'react-native';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { isRTL as isRTLLanguage } from '../constants/languages';
|
|
5
|
+
/**
|
|
6
|
+
* Returns whether the current language is RTL.
|
|
7
|
+
* Checks both I18nManager (native layout) and the current i18n language
|
|
8
|
+
* (covers the session before a restart applies forceRTL).
|
|
9
|
+
*/
|
|
10
|
+
export function useIsRTL() {
|
|
11
|
+
const { i18n } = useTranslation();
|
|
12
|
+
return I18nManager.isRTL || isRTLLanguage(i18n.language);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Returns a text style object that sets the correct text alignment
|
|
16
|
+
* for the current language direction.
|
|
17
|
+
*/
|
|
18
|
+
export function useRTLTextStyle() {
|
|
19
|
+
const isRTL = useIsRTL();
|
|
20
|
+
return useMemo(() => ({
|
|
21
|
+
textAlign: isRTL ? 'right' : 'left',
|
|
22
|
+
writingDirection: isRTL ? 'rtl' : 'ltr',
|
|
23
|
+
}), [isRTL]);
|
|
24
|
+
}
|