sapo-components-ui-rn 1.0.0

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.
Files changed (215) hide show
  1. package/README.md +69 -0
  2. package/dist/assets/error.png +0 -0
  3. package/dist/assets/icon-checkbox-active.svg +4 -0
  4. package/dist/assets/icon-checkbox.svg +3 -0
  5. package/dist/assets/image_default.png +0 -0
  6. package/dist/assets/images/error.png +0 -0
  7. package/dist/assets/images/image_default.png +0 -0
  8. package/dist/assets/images/info.png +0 -0
  9. package/dist/assets/images/success.png +0 -0
  10. package/dist/assets/images/warning.png +0 -0
  11. package/dist/assets/info.png +0 -0
  12. package/dist/assets/success.png +0 -0
  13. package/dist/assets/svg/icon-checkbox-active.svg +4 -0
  14. package/dist/assets/svg/icon-checkbox.svg +3 -0
  15. package/dist/assets/warning.png +0 -0
  16. package/dist/components/ActivityIndicator.d.ts +28 -0
  17. package/dist/components/Avatar/index.d.ts +17 -0
  18. package/dist/components/Badge/index.d.ts +13 -0
  19. package/dist/components/Button/index.d.ts +23 -0
  20. package/dist/components/ButtonIcon/index.d.ts +18 -0
  21. package/dist/components/Checkbox/index.d.ts +14 -0
  22. package/dist/components/ChipBar/index.d.ts +16 -0
  23. package/dist/components/CountingDot/index.d.ts +13 -0
  24. package/dist/components/FloatingButton/index.d.ts +17 -0
  25. package/dist/components/Icon/index.d.ts +17 -0
  26. package/dist/components/IconSvg/index.d.ts +12 -0
  27. package/dist/components/Image/index.d.ts +14 -0
  28. package/dist/components/ProgressBar.d.ts +42 -0
  29. package/dist/components/RadioButton/index.d.ts +13 -0
  30. package/dist/components/ScaleButton/index.d.ts +4 -0
  31. package/dist/components/SelectionField/index.d.ts +24 -0
  32. package/dist/components/Spacer/index.d.ts +9 -0
  33. package/dist/components/Switch/Switch.d.ts +47 -0
  34. package/dist/components/Switch/utils.d.ts +14 -0
  35. package/dist/components/Text/index.d.ts +14 -0
  36. package/dist/components/TextInput/Addons/Outline.d.ts +15 -0
  37. package/dist/components/TextInput/Addons/Underline.d.ts +19 -0
  38. package/dist/components/TextInput/Adornment/TextInputAdornment.d.ts +43 -0
  39. package/dist/components/TextInput/Adornment/TextInputAffix.d.ts +72 -0
  40. package/dist/components/TextInput/Adornment/enums.d.ts +12 -0
  41. package/dist/components/TextInput/Adornment/types.d.ts +11 -0
  42. package/dist/components/TextInput/Adornment/utils.d.ts +11 -0
  43. package/dist/components/TextInput/Label/InputLabel.d.ts +4 -0
  44. package/dist/components/TextInput/Label/LabelBackground.d.ts +4 -0
  45. package/dist/components/TextInput/TextInput.d.ts +171 -0
  46. package/dist/components/TextInput/TextInputDefault.d.ts +4 -0
  47. package/dist/components/TextInput/TextInputFlat.d.ts +4 -0
  48. package/dist/components/TextInput/constants.d.ts +31 -0
  49. package/dist/components/TextInput/helpers.d.ts +95 -0
  50. package/dist/components/TextInput/types.d.ts +154 -0
  51. package/dist/components/Toast/ToastProvider.d.ts +21 -0
  52. package/dist/components/Toast/index.d.ts +3 -0
  53. package/dist/components/Typography/AnimatedText.d.ts +34 -0
  54. package/dist/components/Typography/StyledText.d.ts +12 -0
  55. package/dist/components/Typography/Text.d.ts +35 -0
  56. package/dist/components/Typography/types.d.ts +2 -0
  57. package/dist/components/View/index.d.ts +38 -0
  58. package/dist/components/ViewVisibleAnimated/index.d.ts +25 -0
  59. package/dist/constants.d.ts +1 -0
  60. package/dist/core/theming.d.ts +43 -0
  61. package/dist/icons/IconArrowDown.d.ts +4 -0
  62. package/dist/icons/IconCheckbox.d.ts +4 -0
  63. package/dist/icons/IconCheckboxActive.d.ts +4 -0
  64. package/dist/icons/IconClearText.d.ts +4 -0
  65. package/dist/icons/IconRadio.d.ts +4 -0
  66. package/dist/icons/IconRadioActive.d.ts +4 -0
  67. package/dist/icons/IconRadioDisable.d.ts +4 -0
  68. package/dist/index.d.ts +33 -0
  69. package/dist/index.esm.js +6758 -0
  70. package/dist/index.esm.js.map +1 -0
  71. package/dist/index.js +6823 -0
  72. package/dist/index.js.map +1 -0
  73. package/dist/styles/fonts.d.ts +78 -0
  74. package/dist/styles/overlay.d.ts +3 -0
  75. package/dist/styles/shadow.d.ts +18 -0
  76. package/dist/styles/themes/DarkTheme.d.ts +2 -0
  77. package/dist/styles/themes/LightTheme.d.ts +2 -0
  78. package/dist/styles/themes/index.d.ts +2 -0
  79. package/dist/styles/themes/tokens.d.ts +830 -0
  80. package/dist/theme/blue.d.ts +14 -0
  81. package/dist/theme/colors.d.ts +99 -0
  82. package/dist/theme/container-styles.d.ts +124 -0
  83. package/dist/theme/dimensions.d.ts +36 -0
  84. package/dist/theme/green.d.ts +15 -0
  85. package/dist/theme/icon.d.ts +5 -0
  86. package/dist/theme/ink.d.ts +11 -0
  87. package/dist/theme/orange.d.ts +14 -0
  88. package/dist/theme/purple.d.ts +6 -0
  89. package/dist/theme/red.d.ts +15 -0
  90. package/dist/theme/themes.d.ts +277 -0
  91. package/dist/theme/white.d.ts +10 -0
  92. package/dist/theme/yellow.d.ts +15 -0
  93. package/dist/types/index.d.ts +65 -0
  94. package/dist/types.d.ts +220 -0
  95. package/dist/utils/BackHandler/BackHandler.d.ts +9 -0
  96. package/dist/utils/BackHandler/BackHandler.native.d.ts +2 -0
  97. package/dist/utils/addEventListener.d.ts +12 -0
  98. package/dist/utils/forwardRef.d.ts +11 -0
  99. package/dist/utils/function-utils.d.ts +12 -0
  100. package/dist/utils/getContrastingColor.d.ts +2 -0
  101. package/dist/utils/hasTouchHandler.d.ts +5 -0
  102. package/dist/utils/roundLayoutSize.d.ts +1 -0
  103. package/dist/utils/splitStyles.d.ts +19 -0
  104. package/dist/utils/toast-manager.d.ts +21 -0
  105. package/dist/utils/useAnimatedValue.d.ts +2 -0
  106. package/dist/utils/useAnimatedValueArray.d.ts +2 -0
  107. package/dist/utils/useIsKeyboardShown.d.ts +6 -0
  108. package/dist/utils/useLayout.d.ts +6 -0
  109. package/dist/utils/useLazyRef.d.ts +2 -0
  110. package/package.json +104 -0
  111. package/src/.DS_Store +0 -0
  112. package/src/assets/images/error.png +0 -0
  113. package/src/assets/images/image_default.png +0 -0
  114. package/src/assets/images/info.png +0 -0
  115. package/src/assets/images/success.png +0 -0
  116. package/src/assets/images/warning.png +0 -0
  117. package/src/assets/svg/icon-checkbox-active.svg +4 -0
  118. package/src/assets/svg/icon-checkbox.svg +3 -0
  119. package/src/babel/.eslintrc +7 -0
  120. package/src/babel/index.js +63 -0
  121. package/src/components/.DS_Store +0 -0
  122. package/src/components/ActivityIndicator.tsx +238 -0
  123. package/src/components/Avatar/index.tsx +102 -0
  124. package/src/components/Badge/index.tsx +55 -0
  125. package/src/components/Button/index.tsx +162 -0
  126. package/src/components/ButtonIcon/index.tsx +98 -0
  127. package/src/components/Checkbox/index.tsx +107 -0
  128. package/src/components/ChipBar/index.tsx +112 -0
  129. package/src/components/CountingDot/index.tsx +63 -0
  130. package/src/components/FloatingButton/index.tsx +99 -0
  131. package/src/components/Icon/index.tsx +135 -0
  132. package/src/components/IconSvg/index.tsx +42 -0
  133. package/src/components/Image/index.tsx +127 -0
  134. package/src/components/ProgressBar.tsx +272 -0
  135. package/src/components/RadioButton/index.tsx +105 -0
  136. package/src/components/ScaleButton/index.tsx +17 -0
  137. package/src/components/SelectionField/index.tsx +166 -0
  138. package/src/components/Spacer/index.tsx +19 -0
  139. package/src/components/Switch/Switch.tsx +110 -0
  140. package/src/components/Switch/utils.ts +96 -0
  141. package/src/components/Text/index.tsx +69 -0
  142. package/src/components/TextInput/.DS_Store +0 -0
  143. package/src/components/TextInput/Addons/Outline.tsx +62 -0
  144. package/src/components/TextInput/Addons/Underline.tsx +71 -0
  145. package/src/components/TextInput/Adornment/TextInputAdornment.tsx +191 -0
  146. package/src/components/TextInput/Adornment/TextInputAffix.tsx +212 -0
  147. package/src/components/TextInput/Adornment/enums.tsx +12 -0
  148. package/src/components/TextInput/Adornment/types.tsx +11 -0
  149. package/src/components/TextInput/Adornment/utils.ts +39 -0
  150. package/src/components/TextInput/Label/InputLabel.tsx +216 -0
  151. package/src/components/TextInput/Label/LabelBackground.tsx +100 -0
  152. package/src/components/TextInput/TextInput.tsx +564 -0
  153. package/src/components/TextInput/TextInputDefault.tsx +187 -0
  154. package/src/components/TextInput/TextInputFlat.tsx +452 -0
  155. package/src/components/TextInput/constants.tsx +48 -0
  156. package/src/components/TextInput/helpers.tsx +546 -0
  157. package/src/components/TextInput/types.tsx +155 -0
  158. package/src/components/Toast/ToastProvider.tsx +69 -0
  159. package/src/components/Toast/index.tsx +239 -0
  160. package/src/components/Typography/AnimatedText.tsx +84 -0
  161. package/src/components/Typography/StyledText.tsx +53 -0
  162. package/src/components/Typography/Text.tsx +189 -0
  163. package/src/components/Typography/types.tsx +5 -0
  164. package/src/components/View/index.tsx +175 -0
  165. package/src/components/ViewVisibleAnimated/index.tsx +199 -0
  166. package/src/constants.tsx +16 -0
  167. package/src/core/theming.tsx +144 -0
  168. package/src/icons/IconArrowDown.tsx +14 -0
  169. package/src/icons/IconCheckbox.tsx +20 -0
  170. package/src/icons/IconCheckboxActive.tsx +18 -0
  171. package/src/icons/IconClearText.tsx +14 -0
  172. package/src/icons/IconRadio.tsx +11 -0
  173. package/src/icons/IconRadioActive.tsx +12 -0
  174. package/src/icons/IconRadioDisable.tsx +12 -0
  175. package/src/index.ts +51 -0
  176. package/src/index.tsx +50 -0
  177. package/src/styles/fonts.tsx +89 -0
  178. package/src/styles/overlay.tsx +69 -0
  179. package/src/styles/shadow.tsx +47 -0
  180. package/src/styles/themes/DarkTheme.tsx +157 -0
  181. package/src/styles/themes/LightTheme.tsx +160 -0
  182. package/src/styles/themes/index.ts +3 -0
  183. package/src/styles/themes/tokens.tsx +481 -0
  184. package/src/theme/blue.ts +25 -0
  185. package/src/theme/colors.ts +21 -0
  186. package/src/theme/container-styles.tsx +126 -0
  187. package/src/theme/dimensions.ts +44 -0
  188. package/src/theme/green.ts +27 -0
  189. package/src/theme/icon.ts +7 -0
  190. package/src/theme/ink.ts +18 -0
  191. package/src/theme/orange.ts +24 -0
  192. package/src/theme/purple.ts +8 -0
  193. package/src/theme/red.ts +26 -0
  194. package/src/theme/themes.tsx +287 -0
  195. package/src/theme/white.ts +16 -0
  196. package/src/theme/yellow.ts +26 -0
  197. package/src/types/index.ts +71 -0
  198. package/src/types/svg.d.ts +5 -0
  199. package/src/types.ts +259 -0
  200. package/src/types.tsx +284 -0
  201. package/src/utils/BackHandler/BackHandler.native.tsx +3 -0
  202. package/src/utils/BackHandler/BackHandler.tsx +11 -0
  203. package/src/utils/addEventListener.tsx +56 -0
  204. package/src/utils/forwardRef.tsx +23 -0
  205. package/src/utils/function-utils.tsx +108 -0
  206. package/src/utils/getContrastingColor.tsx +15 -0
  207. package/src/utils/hasTouchHandler.tsx +23 -0
  208. package/src/utils/roundLayoutSize.ts +2 -0
  209. package/src/utils/splitStyles.ts +60 -0
  210. package/src/utils/toast-manager.tsx +46 -0
  211. package/src/utils/useAnimatedValue.tsx +9 -0
  212. package/src/utils/useAnimatedValueArray.tsx +13 -0
  213. package/src/utils/useIsKeyboardShown.tsx +55 -0
  214. package/src/utils/useLayout.tsx +29 -0
  215. package/src/utils/useLazyRef.tsx +11 -0
@@ -0,0 +1,69 @@
1
+ import React, { forwardRef, useImperativeHandle } from "react";
2
+ import { SafeAreaProvider } from "react-native-safe-area-context";
3
+ import Toast from "./index";
4
+ import { toastRef } from "../../utils/toast-manager";
5
+
6
+ interface ToastRef {
7
+ show: (params: {
8
+ message: string;
9
+ duration?: number;
10
+ position?: "top" | "bottom";
11
+ type?: "success" | "fail";
12
+ onPress?: () => void;
13
+ title?: string;
14
+ }) => void;
15
+ }
16
+
17
+ interface ToastProviderProps {
18
+ children: React.ReactNode;
19
+ /**
20
+ * Nếu ứng dụng đã có SafeAreaProvider, set wrapSafeArea = false
21
+ * @default true
22
+ */
23
+ wrapSafeArea?: boolean;
24
+ }
25
+
26
+ const ToastProvider = forwardRef<ToastRef, ToastProviderProps>(
27
+ ({ children, wrapSafeArea = true }, ref) => {
28
+ const toastComponentRef = React.useRef<ToastRef>(null);
29
+
30
+ // Expose the show method through the ref
31
+ useImperativeHandle(ref, () => ({
32
+ show: (params) => {
33
+ if (toastComponentRef.current) {
34
+ toastComponentRef.current.show(params);
35
+ }
36
+ },
37
+ }));
38
+
39
+ // Update the global toastRef when component mounts
40
+ React.useEffect(() => {
41
+ if (toastComponentRef.current) {
42
+ toastRef.current = toastComponentRef.current;
43
+ }
44
+ return () => {
45
+ toastRef.current = null;
46
+ };
47
+ }, []);
48
+
49
+ const renderToast = () => {
50
+ if (wrapSafeArea) {
51
+ return (
52
+ <SafeAreaProvider>
53
+ <Toast ref={toastComponentRef} />
54
+ </SafeAreaProvider>
55
+ );
56
+ }
57
+ return <Toast ref={toastComponentRef} />;
58
+ };
59
+
60
+ return (
61
+ <>
62
+ {children}
63
+ {renderToast()}
64
+ </>
65
+ );
66
+ }
67
+ );
68
+
69
+ export default ToastProvider;
@@ -0,0 +1,239 @@
1
+ /* eslint-disable react-hooks/exhaustive-deps */
2
+
3
+ import { CONSTANTS } from "../../styles/themes/tokens";
4
+ import React, { useImperativeHandle, useRef, useState } from "react";
5
+ import { StyleSheet, Image as RNImage } from "react-native";
6
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
7
+ import Text from "../Text";
8
+ import View from "../View";
9
+
10
+ import { memoWithRef } from "@/utils/function-utils";
11
+ import ViewVisibleAnimated from "../ViewVisibleAnimated";
12
+ import { useInternalTheme } from "../../core/theming";
13
+
14
+ const POSITION = {
15
+ TOP: "top",
16
+ BOTTOM: "bottom",
17
+ } as const;
18
+
19
+ type Position = (typeof POSITION)[keyof typeof POSITION];
20
+ type ToastType = "success" | "error" | "info" | "warning";
21
+
22
+ interface Notification {
23
+ message?: string;
24
+ duration?: number;
25
+ position?: Position;
26
+ type?: ToastType;
27
+ title: string;
28
+ onPress?: () => void;
29
+ }
30
+
31
+ interface ToastOptions {
32
+ position: Position;
33
+ type: ToastType;
34
+ title?: string;
35
+ onPress?: () => void;
36
+ }
37
+
38
+ interface ToastRef {
39
+ show: (options: Notification) => void;
40
+ }
41
+
42
+ interface ViewVisibleAnimatedRef {
43
+ show: (callback?: () => void, duration?: number) => void;
44
+ hide: (callback?: () => void, duration?: number) => void;
45
+ }
46
+
47
+ const Toast = memoWithRef(
48
+ (_props: Record<string, never>, ref: React.ForwardedRef<ToastRef>) => {
49
+ const [message, setMessage] = useState<string>("Thông báo");
50
+ const viewVisibleAnimatedRef = useRef<ViewVisibleAnimatedRef>(null);
51
+ const { bottom, top } = useSafeAreaInsets();
52
+ const [options, setOptions] = useState<ToastOptions>({
53
+ position: POSITION.TOP,
54
+ type: "success",
55
+ title: "Thông báo",
56
+ });
57
+
58
+ const theme = useInternalTheme();
59
+ const { colors } = theme;
60
+
61
+ const TIME_OUT = useRef<NodeJS.Timeout | null>(null);
62
+
63
+ const show = ({
64
+ message,
65
+ duration = 1000,
66
+ position = POSITION.TOP,
67
+ type = "success",
68
+ onPress,
69
+ title = "Thông báo",
70
+ }: Notification) => {
71
+ if (!TIME_OUT.current) {
72
+ handleShow({ message, position, type, onPress, title, duration });
73
+ return;
74
+ }
75
+
76
+ clearTimeout(TIME_OUT.current);
77
+ hide(() => {
78
+ handleShow({
79
+ message,
80
+ position,
81
+ type,
82
+ onPress,
83
+ title,
84
+ duration,
85
+ });
86
+ }, true);
87
+ };
88
+
89
+ useImperativeHandle(
90
+ ref,
91
+ () => ({
92
+ show,
93
+ }),
94
+ []
95
+ );
96
+
97
+ const handleShow = ({
98
+ message,
99
+ position = POSITION.TOP,
100
+ type = "success",
101
+ onPress,
102
+ title = "Thông báo",
103
+ duration = 1000,
104
+ }: Notification) => {
105
+ setMessage(message || "");
106
+ setOptions({ position, type, onPress, title });
107
+
108
+ viewVisibleAnimatedRef.current?.show?.(() => {
109
+ TIME_OUT.current = setTimeout(() => {
110
+ hide(() => {}, false);
111
+ }, duration);
112
+ }, 100);
113
+ };
114
+
115
+ const hide = (callback: () => void, skipSetMessage: boolean) => {
116
+ viewVisibleAnimatedRef.current?.hide?.(() => {
117
+ if (!skipSetMessage) {
118
+ setMessage("Thông báo");
119
+ }
120
+ callback?.();
121
+ }, 300);
122
+ };
123
+
124
+ const onPressToast = () => {
125
+ hide(() => {}, false);
126
+ options?.onPress?.();
127
+ };
128
+
129
+ const getBackgroundColor = () => {
130
+ switch (options.type) {
131
+ case "success":
132
+ return colors.surfaceSuccessDefault;
133
+ case "error":
134
+ return colors.surfaceErrorDefault;
135
+ case "info":
136
+ return colors.surfacePrimaryInverseDefault;
137
+ case "warning":
138
+ return colors.surfaceWarningDefault;
139
+ default:
140
+ return colors.surfaceSuccessDefault;
141
+ }
142
+ };
143
+
144
+ const getSourceIcon = () => {
145
+ switch (options.type) {
146
+ case "success":
147
+ return require("./assets/images/success.png");
148
+ case "error":
149
+ return require("./assets/images/error.png");
150
+ case "info":
151
+ return require("./assets/images/info.png");
152
+ case "warning":
153
+ return require("./assets/images/warning.png");
154
+ default:
155
+ return require("./assets/images/success.png");
156
+ }
157
+ };
158
+
159
+ return (
160
+ <ViewVisibleAnimated
161
+ ref={viewVisibleAnimatedRef}
162
+ autoShow={false}
163
+ scaleEnable
164
+ style={[
165
+ styles.container,
166
+ options.position === POSITION.BOTTOM && {
167
+ bottom: bottom ? bottom : CONSTANTS.SPACE_16,
168
+ },
169
+ options.position === POSITION.TOP && {
170
+ top: top ? top : CONSTANTS.SPACE_16,
171
+ },
172
+ ]}
173
+ >
174
+ <View
175
+ row={!options.title}
176
+ center={!options.title}
177
+ full
178
+ onPress={onPressToast}
179
+ style={[
180
+ styles.wrapContent,
181
+ {
182
+ shadowColor: "black",
183
+ shadowOffset: {
184
+ width: 1,
185
+ height: 2,
186
+ },
187
+ shadowOpacity: 0.3,
188
+ shadowRadius: 5,
189
+ backgroundColor: getBackgroundColor(),
190
+ },
191
+ ]}
192
+ >
193
+ <View row style={{ alignItems: "flex-start" }}>
194
+ <View>
195
+ <RNImage source={getSourceIcon()} style={styles.img} />
196
+ </View>
197
+ <View full paddingLeft={CONSTANTS.SPACE_8}>
198
+ <Text bold color={colors.textOnFillDefault}>
199
+ {options.title}
200
+ </Text>
201
+ {message.toString().length > 0 && (
202
+ <Text
203
+ color={colors.textOnFillDefault}
204
+ numberOfLines={4}
205
+ style={!options.title && styles.wrapMess}
206
+ >
207
+ {message}
208
+ </Text>
209
+ )}
210
+ </View>
211
+ </View>
212
+ </View>
213
+ </ViewVisibleAnimated>
214
+ );
215
+ }
216
+ );
217
+
218
+ export default Toast;
219
+
220
+ const styles = StyleSheet.create({
221
+ wrapMess: { marginLeft: CONSTANTS.SPACE_8, flex: 1 },
222
+ wrapContent: {
223
+ borderRadius: CONSTANTS.BORDER_RADIUS_12,
224
+ padding: CONSTANTS.SPACE_12,
225
+ width: CONSTANTS.DEVICE_WIDTH - CONSTANTS.SPACE_16 * 2,
226
+ marginHorizontal: CONSTANTS.SPACE_16,
227
+ },
228
+ container: {
229
+ position: "absolute",
230
+ left: 0,
231
+ right: 0,
232
+ alignItems: "center",
233
+ width: CONSTANTS.DEVICE_WIDTH,
234
+ },
235
+ img: {
236
+ width: 20,
237
+ height: 20,
238
+ },
239
+ });
@@ -0,0 +1,84 @@
1
+ import * as React from "react";
2
+ import {
3
+ Animated,
4
+ I18nManager,
5
+ StyleProp,
6
+ StyleSheet,
7
+ TextStyle,
8
+ Text,
9
+ } from "react-native";
10
+
11
+ import type { VariantProp } from "./types";
12
+ import { useInternalTheme } from "../../core/theming";
13
+ import type { ThemeProp } from "../../types";
14
+ import { forwardRef } from "../../utils/forwardRef";
15
+
16
+ type Props<T> = React.ComponentPropsWithRef<typeof Animated.Text> & {
17
+ /**
18
+ * Variant defines appropriate text styles for type role and its size.
19
+ * Available variants:
20
+ *
21
+ * Display: `displayLarge`, `displayMedium`, `displaySmall`
22
+ *
23
+ * Headline: `headlineLarge`, `headlineMedium`, `headlineSmall`
24
+ *
25
+ * Title: `titleLarge`, `titleMedium`, `titleSmall`
26
+ *
27
+ * Label: `labelLarge`, `labelMedium`, `labelSmall`
28
+ *
29
+ * Body: `bodyLarge`, `bodyMedium`, `bodySmall`
30
+ */
31
+ variant?: VariantProp<T>;
32
+ style?: StyleProp<TextStyle>;
33
+ /**
34
+ * @optional
35
+ */
36
+ theme?: ThemeProp;
37
+ };
38
+
39
+ /**
40
+ * Animated text component which follows styles from the theme.
41
+ *
42
+ * @extends Text props https://reactnative.dev/docs/text#props
43
+ */
44
+ const AnimatedText = forwardRef<Text & HTMLElement, Props<never>>(
45
+ function AnimatedText({ style, variant, ...rest }, ref) {
46
+ const theme = useInternalTheme();
47
+ const writingDirection = I18nManager.getConstants().isRTL ? "rtl" : "ltr";
48
+
49
+ if (variant) {
50
+ const font = theme.fonts[variant];
51
+ if (typeof font !== "object") {
52
+ throw new Error(
53
+ `Variant ${variant} was not provided properly. Valid variants are ${Object.keys(
54
+ theme.fonts
55
+ ).join(", ")}.`
56
+ );
57
+ }
58
+
59
+ return (
60
+ <Animated.Text
61
+ ref={ref}
62
+ {...rest}
63
+ style={[
64
+ font,
65
+ styles.text,
66
+ { writingDirection, color: theme.colors.backgroundPrimary },
67
+ style,
68
+ ]}
69
+ />
70
+ );
71
+ }
72
+ }
73
+ );
74
+
75
+ const styles = StyleSheet.create({
76
+ text: {
77
+ textAlign: "left",
78
+ },
79
+ });
80
+
81
+ export const customAnimatedText = <T,>() =>
82
+ AnimatedText as (props: Props<T>) => JSX.Element;
83
+
84
+ export default AnimatedText;
@@ -0,0 +1,53 @@
1
+ import * as React from "react";
2
+ import { I18nManager, StyleProp, StyleSheet, TextStyle } from "react-native";
3
+
4
+ import color from "color";
5
+
6
+ import Text from "./Text";
7
+ import type { ThemeProp } from "../../types";
8
+ import { useInternalTheme } from "../../core/theming";
9
+
10
+ type Props = React.ComponentProps<typeof Text> & {
11
+ alpha?: number;
12
+ family: "regular" | "medium" | "light" | "thin";
13
+ style?: StyleProp<TextStyle>;
14
+ theme?: ThemeProp;
15
+ };
16
+
17
+ const StyledText = ({
18
+ alpha = 1,
19
+ family,
20
+ style,
21
+ theme: themeOverrides,
22
+ ...rest
23
+ }: Props) => {
24
+ const theme = useInternalTheme();
25
+
26
+ const textColor = color(theme.colors.backgroundPrimary)
27
+ .alpha(alpha)
28
+ .rgb()
29
+ .string();
30
+ const writingDirection = I18nManager.getConstants().isRTL ? "rtl" : "ltr";
31
+
32
+ return (
33
+ <Text
34
+ {...rest}
35
+ style={[
36
+ styles.text,
37
+ {
38
+ color: textColor,
39
+ writingDirection,
40
+ },
41
+ style,
42
+ ]}
43
+ />
44
+ );
45
+ };
46
+
47
+ const styles = StyleSheet.create({
48
+ text: {
49
+ textAlign: "left",
50
+ },
51
+ });
52
+
53
+ export default StyledText;
@@ -0,0 +1,189 @@
1
+ import * as React from "react";
2
+ import {
3
+ I18nManager,
4
+ StyleProp,
5
+ StyleSheet,
6
+ Text as NativeText,
7
+ TextStyle,
8
+ } from "react-native";
9
+
10
+ import AnimatedText from "./AnimatedText";
11
+ import type { VariantProp } from "./types";
12
+ import StyledText from "./StyledText";
13
+ import { useInternalTheme } from "../../core/theming";
14
+ import type { ThemeProp } from "../../types";
15
+ import { forwardRef } from "../../utils/forwardRef";
16
+
17
+ export type Props<T> = React.ComponentProps<typeof NativeText> & {
18
+ /**
19
+ * @supported Available in v5.x with theme version 3
20
+ *
21
+ * Variant defines appropriate text styles for type role and its size.
22
+ * Available variants:
23
+ *
24
+ * Display: `displayLarge`, `displayMedium`, `displaySmall`
25
+ *
26
+ * Headline: `headlineLarge`, `headlineMedium`, `headlineSmall`
27
+ *
28
+ * Title: `titleLarge`, `titleMedium`, `titleSmall`
29
+ *
30
+ * Label: `labelLarge`, `labelMedium`, `labelSmall`
31
+ *
32
+ * Body: `bodyLarge`, `bodyMedium`, `bodySmall`
33
+ */
34
+ variant?: VariantProp<T>;
35
+ children: React.ReactNode;
36
+ theme?: ThemeProp;
37
+ style?: StyleProp<TextStyle>;
38
+ };
39
+
40
+ export type TextRef = React.ForwardedRef<{
41
+ setNativeProps(args: Object): void;
42
+ }>;
43
+
44
+ // @component-group Typography
45
+
46
+ /**
47
+ * Typography component showing styles complied with passed `variant` prop and supported by the type system.
48
+ *
49
+ * ## Usage
50
+ * ```js
51
+ * import * as React from 'react';
52
+ * import { Text } from 'react-native-paper';
53
+ *
54
+ * const MyComponent = () => (
55
+ * <>
56
+ * <Text variant="displayLarge">Display Large</Text>
57
+ * <Text variant="displayMedium">Display Medium</Text>
58
+ * <Text variant="displaySmall">Display small</Text>
59
+ *
60
+ * <Text variant="headlineLarge">Headline Large</Text>
61
+ * <Text variant="headlineMedium">Headline Medium</Text>
62
+ * <Text variant="headlineSmall">Headline Small</Text>
63
+ *
64
+ * <Text variant="titleLarge">Title Large</Text>
65
+ * <Text variant="titleMedium">Title Medium</Text>
66
+ * <Text variant="titleSmall">Title Small</Text>
67
+ *
68
+ * <Text variant="bodyLarge">Body Large</Text>
69
+ * <Text variant="bodyMedium">Body Medium</Text>
70
+ * <Text variant="bodySmall">Body Small</Text>
71
+ *
72
+ * <Text variant="labelLarge">Label Large</Text>
73
+ * <Text variant="labelMedium">Label Medium</Text>
74
+ * <Text variant="labelSmall">Label Small</Text>
75
+ * </>
76
+ * );
77
+ *
78
+ * export default MyComponent;
79
+ * ```
80
+ *
81
+ * @extends Text props https://reactnative.dev/docs/text#props
82
+ */
83
+ const Text = (
84
+ { style, variant, theme: initialTheme, ...rest }: Props<string>,
85
+ ref: TextRef
86
+ ) => {
87
+ const root = React.useRef<NativeText | null>(null);
88
+ // FIXME: destructure it in TS 4.6+
89
+ const theme = useInternalTheme();
90
+ const writingDirection = I18nManager.getConstants().isRTL ? "rtl" : "ltr";
91
+
92
+ React.useImperativeHandle(ref, () => ({
93
+ setNativeProps: (args: Object) => root.current?.setNativeProps(args),
94
+ }));
95
+
96
+ if (variant) {
97
+ let font = theme.fonts[variant];
98
+ let textStyle = [font, style];
99
+
100
+ if (
101
+ React.isValidElement(rest.children) &&
102
+ (rest.children.type === Component ||
103
+ rest.children.type === AnimatedText ||
104
+ rest.children.type === StyledText)
105
+ ) {
106
+ const { props } = rest.children;
107
+
108
+ // Context: Some components have the built-in `Text` component with a predefined variant,
109
+ // that also accepts `children` as a `React.Node`. This can result in a situation,
110
+ // where another `Text` component is rendered within the built-in `Text` component.
111
+ // By doing that, we assume that user doesn't want to consume pre-defined font properties.
112
+ // Case one: Nested `Text` has different `variant` that specified in parent. For example:
113
+ // <Chip>
114
+ // <Text variant="displayMedium">Nested</Text>
115
+ // </Chip>
116
+ // Solution: To address the following scenario, the code below overrides the `variant`
117
+ // specified in a parent in favor of children's variant:
118
+ //@ts-ignore
119
+ if (props.variant) {
120
+ //@ts-ignore
121
+ font = theme.fonts[props.variant as VariantProp<typeof props.variant>];
122
+ textStyle = [style, font];
123
+ }
124
+
125
+ // Case two: Nested `Text` has specified `styles` which intefere
126
+ // with font properties, from the parent's `variant`. For example:
127
+ // <Chip>
128
+ // <Text style={{fontSize: 30}}>Nested</Text>
129
+ // </Chip>
130
+ // Solution: To address the following scenario, the code below overrides the
131
+ // parent's style with children's style:
132
+ //@ts-ignore
133
+ if (!props.variant) {
134
+ //@ts-ignore
135
+ textStyle = [style, props.style];
136
+ }
137
+ }
138
+
139
+ if (typeof font !== "object") {
140
+ throw new Error(
141
+ `Variant ${variant} was not provided properly. Valid variants are ${Object.keys(
142
+ theme.fonts
143
+ ).join(", ")}.`
144
+ );
145
+ }
146
+
147
+ return (
148
+ <NativeText
149
+ ref={root}
150
+ style={[
151
+ styles.text,
152
+ { writingDirection, color: theme.colors.backgroundPrimary },
153
+ textStyle,
154
+ ]}
155
+ {...rest}
156
+ />
157
+ );
158
+ } else {
159
+ const font = theme.fonts.default;
160
+ const textStyle = {
161
+ ...font,
162
+ color: theme.colors?.backgroundPrimary,
163
+ };
164
+ return (
165
+ <NativeText
166
+ {...rest}
167
+ ref={root}
168
+ style={[styles.text, textStyle, { writingDirection }, style]}
169
+ />
170
+ );
171
+ }
172
+ };
173
+
174
+ const styles = StyleSheet.create({
175
+ text: {
176
+ textAlign: "left",
177
+ },
178
+ });
179
+
180
+ type TextComponent<T> = (
181
+ props: Props<T> & { ref?: React.RefObject<TextRef> }
182
+ //@ts-ignore
183
+ ) => JSX.Element;
184
+
185
+ const Component = forwardRef(Text) as TextComponent<never>;
186
+
187
+ export const customText = <T,>() => Component as unknown as TextComponent<T>;
188
+
189
+ export default Component;
@@ -0,0 +1,5 @@
1
+ import type { MD3TypescaleKey } from '../../types';
2
+
3
+ export type VariantProp<T> =
4
+ | (T extends string ? (string extends T ? never : T) : never)
5
+ | keyof typeof MD3TypescaleKey;