ignite-parse-auth-kit 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.
- package/CONTRIBUTING.md +0 -0
- package/LICENSE +21 -0
- package/README.md +492 -0
- package/app/app.tsx +116 -0
- package/app/components/AlertTongle.tsx +105 -0
- package/app/components/AutoImage.tsx +89 -0
- package/app/components/Button.tsx +248 -0
- package/app/components/Card.tsx +314 -0
- package/app/components/EmptyState.tsx +248 -0
- package/app/components/Header.tsx +332 -0
- package/app/components/Icon.tsx +140 -0
- package/app/components/ListItem.tsx +243 -0
- package/app/components/ListView.tsx +42 -0
- package/app/components/Screen.tsx +305 -0
- package/app/components/Text.test.tsx +23 -0
- package/app/components/Text.tsx +116 -0
- package/app/components/TextField.tsx +292 -0
- package/app/components/Toggle/Checkbox.tsx +123 -0
- package/app/components/Toggle/Radio.tsx +106 -0
- package/app/components/Toggle/Switch.tsx +264 -0
- package/app/components/Toggle/Toggle.tsx +285 -0
- package/app/components/index copy.ts +15 -0
- package/app/components/index.ts +18 -0
- package/app/config/config.base.ts +26 -0
- package/app/config/config.dev.ts +10 -0
- package/app/config/config.prod.ts +10 -0
- package/app/config/index.ts +28 -0
- package/app/context/AuthContext.tsx +14 -0
- package/app/context/EpisodeContext.tsx +136 -0
- package/app/context/auth/AuthProvider.tsx +340 -0
- package/app/context/auth/hooks.ts +29 -0
- package/app/context/auth/index.ts +38 -0
- package/app/context/auth/reducer.ts +68 -0
- package/app/context/auth/services.ts +394 -0
- package/app/context/auth/types.ts +99 -0
- package/app/context/auth/validation.ts +45 -0
- package/app/devtools/ReactotronClient.ts +9 -0
- package/app/devtools/ReactotronClient.web.ts +12 -0
- package/app/devtools/ReactotronConfig.ts +139 -0
- package/app/i18n/ar.ts +126 -0
- package/app/i18n/demo-ar.ts +464 -0
- package/app/i18n/demo-en.ts +462 -0
- package/app/i18n/demo-es.ts +469 -0
- package/app/i18n/demo-fr.ts +471 -0
- package/app/i18n/demo-hi.ts +468 -0
- package/app/i18n/demo-ja.ts +464 -0
- package/app/i18n/demo-ko.ts +457 -0
- package/app/i18n/en.ts +146 -0
- package/app/i18n/es.ts +132 -0
- package/app/i18n/fr.ts +132 -0
- package/app/i18n/hi.ts +131 -0
- package/app/i18n/index.ts +86 -0
- package/app/i18n/ja.ts +130 -0
- package/app/i18n/ko.ts +129 -0
- package/app/i18n/translate.ts +33 -0
- package/app/lib/Parse/index.ts +2 -0
- package/app/lib/Parse/parse.ts +62 -0
- package/app/navigators/AppNavigator.tsx +145 -0
- package/app/navigators/DemoNavigator.tsx +137 -0
- package/app/navigators/navigationUtilities.ts +208 -0
- package/app/screens/ChooseAuthScreen.tsx +224 -0
- package/app/screens/DemoCommunityScreen.tsx +141 -0
- package/app/screens/DemoDebugScreen.tsx +192 -0
- package/app/screens/DemoPodcastListScreen.tsx +387 -0
- package/app/screens/DemoShowroomScreen/DemoDivider.tsx +66 -0
- package/app/screens/DemoShowroomScreen/DemoShowroomScreen.tsx +313 -0
- package/app/screens/DemoShowroomScreen/DemoUseCase.tsx +52 -0
- package/app/screens/DemoShowroomScreen/DrawerIconButton.tsx +120 -0
- package/app/screens/DemoShowroomScreen/SectionListWithKeyboardAwareScrollView.tsx +59 -0
- package/app/screens/DemoShowroomScreen/demos/DemoAutoImage.tsx +230 -0
- package/app/screens/DemoShowroomScreen/demos/DemoButton.tsx +234 -0
- package/app/screens/DemoShowroomScreen/demos/DemoCard.tsx +181 -0
- package/app/screens/DemoShowroomScreen/demos/DemoEmptyState.tsx +78 -0
- package/app/screens/DemoShowroomScreen/demos/DemoHeader.tsx +151 -0
- package/app/screens/DemoShowroomScreen/demos/DemoIcon.tsx +115 -0
- package/app/screens/DemoShowroomScreen/demos/DemoListItem.tsx +218 -0
- package/app/screens/DemoShowroomScreen/demos/DemoText.tsx +144 -0
- package/app/screens/DemoShowroomScreen/demos/DemoTextField.tsx +233 -0
- package/app/screens/DemoShowroomScreen/demos/DemoToggle.tsx +354 -0
- package/app/screens/DemoShowroomScreen/demos/index.ts +12 -0
- package/app/screens/ErrorScreen/ErrorBoundary.tsx +76 -0
- package/app/screens/ErrorScreen/ErrorDetails.tsx +98 -0
- package/app/screens/ForgetPasswordScreen.tsx +180 -0
- package/app/screens/LoginScreen.tsx +260 -0
- package/app/screens/RegisterScreen.tsx +395 -0
- package/app/screens/WelcomeScreen.tsx +114 -0
- package/app/services/api/apiProblem.test.ts +73 -0
- package/app/services/api/apiProblem.ts +74 -0
- package/app/services/api/index.ts +91 -0
- package/app/services/api/types.ts +50 -0
- package/app/theme/colors.ts +85 -0
- package/app/theme/colorsDark.ts +50 -0
- package/app/theme/context.tsx +145 -0
- package/app/theme/context.utils.ts +25 -0
- package/app/theme/spacing.ts +14 -0
- package/app/theme/spacingDark.ts +14 -0
- package/app/theme/styles.ts +24 -0
- package/app/theme/theme.ts +23 -0
- package/app/theme/timing.ts +6 -0
- package/app/theme/types.ts +64 -0
- package/app/theme/typography.ts +71 -0
- package/app/utils/crashReporting.ts +62 -0
- package/app/utils/delay.ts +6 -0
- package/app/utils/formatDate.ts +49 -0
- package/app/utils/gestureHandler.native.ts +3 -0
- package/app/utils/gestureHandler.ts +6 -0
- package/app/utils/hasValidStringProp.ts +11 -0
- package/app/utils/openLinkInBrowser.ts +8 -0
- package/app/utils/storage/index.ts +82 -0
- package/app/utils/storage/storage.test.ts +61 -0
- package/app/utils/useHeader.tsx +37 -0
- package/app/utils/useIsMounted.ts +18 -0
- package/app/utils/useSafeAreaInsetsStyle.ts +46 -0
- package/app.config.ts +39 -0
- package/app.json +67 -0
- package/assets/icons/back.png +0 -0
- package/assets/icons/back@2x.png +0 -0
- package/assets/icons/back@3x.png +0 -0
- package/assets/icons/bell.png +0 -0
- package/assets/icons/bell@2x.png +0 -0
- package/assets/icons/bell@3x.png +0 -0
- package/assets/icons/caretLeft.png +0 -0
- package/assets/icons/caretLeft@2x.png +0 -0
- package/assets/icons/caretLeft@3x.png +0 -0
- package/assets/icons/caretRight.png +0 -0
- package/assets/icons/caretRight@2x.png +0 -0
- package/assets/icons/caretRight@3x.png +0 -0
- package/assets/icons/check.png +0 -0
- package/assets/icons/check@2x.png +0 -0
- package/assets/icons/check@3x.png +0 -0
- package/assets/icons/demo/clap.png +0 -0
- package/assets/icons/demo/clap@2x.png +0 -0
- package/assets/icons/demo/clap@3x.png +0 -0
- package/assets/icons/demo/community.png +0 -0
- package/assets/icons/demo/community@2x.png +0 -0
- package/assets/icons/demo/community@3x.png +0 -0
- package/assets/icons/demo/components.png +0 -0
- package/assets/icons/demo/components@2x.png +0 -0
- package/assets/icons/demo/components@3x.png +0 -0
- package/assets/icons/demo/debug.png +0 -0
- package/assets/icons/demo/debug@2x.png +0 -0
- package/assets/icons/demo/debug@3x.png +0 -0
- package/assets/icons/demo/github.png +0 -0
- package/assets/icons/demo/github@2x.png +0 -0
- package/assets/icons/demo/github@3x.png +0 -0
- package/assets/icons/demo/heart.png +0 -0
- package/assets/icons/demo/heart@2x.png +0 -0
- package/assets/icons/demo/heart@3x.png +0 -0
- package/assets/icons/demo/pin.png +0 -0
- package/assets/icons/demo/pin@2x.png +0 -0
- package/assets/icons/demo/pin@3x.png +0 -0
- package/assets/icons/demo/podcast.png +0 -0
- package/assets/icons/demo/podcast@2x.png +0 -0
- package/assets/icons/demo/podcast@3x.png +0 -0
- package/assets/icons/demo/slack.png +0 -0
- package/assets/icons/demo/slack@2x.png +0 -0
- package/assets/icons/demo/slack@3x.png +0 -0
- package/assets/icons/google.png +0 -0
- package/assets/icons/hidden.png +0 -0
- package/assets/icons/hidden@2x.png +0 -0
- package/assets/icons/hidden@3x.png +0 -0
- package/assets/icons/ladybug.png +0 -0
- package/assets/icons/ladybug@2x.png +0 -0
- package/assets/icons/ladybug@3x.png +0 -0
- package/assets/icons/lock.png +0 -0
- package/assets/icons/lock@2x.png +0 -0
- package/assets/icons/lock@3x.png +0 -0
- package/assets/icons/menu.png +0 -0
- package/assets/icons/menu@2x.png +0 -0
- package/assets/icons/menu@3x.png +0 -0
- package/assets/icons/more.png +0 -0
- package/assets/icons/more@2x.png +0 -0
- package/assets/icons/more@3x.png +0 -0
- package/assets/icons/settings.png +0 -0
- package/assets/icons/settings@2x.png +0 -0
- package/assets/icons/settings@3x.png +0 -0
- package/assets/icons/view.png +0 -0
- package/assets/icons/view@2x.png +0 -0
- package/assets/icons/view@3x.png +0 -0
- package/assets/icons/x.png +0 -0
- package/assets/icons/x@2x.png +0 -0
- package/assets/icons/x@3x.png +0 -0
- package/assets/images/app-icon-all.png +0 -0
- package/assets/images/app-icon-android-adaptive-background.png +0 -0
- package/assets/images/app-icon-android-adaptive-foreground.png +0 -0
- package/assets/images/app-icon-android-legacy.png +0 -0
- package/assets/images/app-icon-ios.png +0 -0
- package/assets/images/app-icon-web-favicon.png +0 -0
- package/assets/images/demo/cr-logo.png +0 -0
- package/assets/images/demo/cr-logo@2x.png +0 -0
- package/assets/images/demo/cr-logo@3x.png +0 -0
- package/assets/images/demo/rnl-logo.png +0 -0
- package/assets/images/demo/rnl-logo@2x.png +0 -0
- package/assets/images/demo/rnl-logo@3x.png +0 -0
- package/assets/images/demo/rnn-logo.png +0 -0
- package/assets/images/demo/rnn-logo@2x.png +0 -0
- package/assets/images/demo/rnn-logo@3x.png +0 -0
- package/assets/images/demo/rnr-image-1.png +0 -0
- package/assets/images/demo/rnr-image-1@2x.png +0 -0
- package/assets/images/demo/rnr-image-1@3x.png +0 -0
- package/assets/images/demo/rnr-image-2.png +0 -0
- package/assets/images/demo/rnr-image-2@2x.png +0 -0
- package/assets/images/demo/rnr-image-2@3x.png +0 -0
- package/assets/images/demo/rnr-image-3.png +0 -0
- package/assets/images/demo/rnr-image-3@2x.png +0 -0
- package/assets/images/demo/rnr-image-3@3x.png +0 -0
- package/assets/images/demo/rnr-logo.png +0 -0
- package/assets/images/demo/rnr-logo@2x.png +0 -0
- package/assets/images/demo/rnr-logo@3x.png +0 -0
- package/assets/images/logo.png +0 -0
- package/assets/images/logo@2x.png +0 -0
- package/assets/images/logo@3x.png +0 -0
- package/assets/images/sad-face.png +0 -0
- package/assets/images/sad-face@2x.png +0 -0
- package/assets/images/sad-face@3x.png +0 -0
- package/assets/images/welcome-face.png +0 -0
- package/assets/images/welcome-face@2x.png +0 -0
- package/assets/images/welcome-face@3x.png +0 -0
- package/babel.config.js +7 -0
- package/bin/cli.js +196 -0
- package/ignite/templates/app-icon/android-adaptive-background.png +0 -0
- package/ignite/templates/app-icon/android-adaptive-foreground.png +0 -0
- package/ignite/templates/app-icon/android-legacy.png +0 -0
- package/ignite/templates/app-icon/ios-universal.png +0 -0
- package/ignite/templates/component/NAME.tsx.ejs +39 -0
- package/ignite/templates/navigator/NAMENavigator.tsx.ejs +18 -0
- package/ignite/templates/screen/NAMEScreen.tsx.ejs +29 -0
- package/ignite/templates/splash-screen/logo.png +0 -0
- package/index.tsx +9 -0
- package/jest.config.js +5 -0
- package/metro.config.js +31 -0
- package/package.json +166 -0
- package/plugins/withSplashScreen.ts +69 -0
- package/src/app/_layout.tsx +58 -0
- package/src/app/index.tsx +5 -0
- package/test/i18n.test.ts +75 -0
- package/test/mockFile.ts +6 -0
- package/test/setup.ts +58 -0
- package/test/test-tsconfig.json +8 -0
- package/tsconfig.json +52 -0
- package/types/lib.es5.d.ts +25 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ForwardedRef, forwardRef, PropsWithoutRef, ReactElement, RefObject } from "react"
|
|
2
|
+
import { FlatList } from "react-native"
|
|
3
|
+
import { FlashList, FlashListProps } from "@shopify/flash-list"
|
|
4
|
+
|
|
5
|
+
import { isRTL } from "@/i18n"
|
|
6
|
+
|
|
7
|
+
export type ListViewRef<T> = FlashList<T> | FlatList<T>
|
|
8
|
+
|
|
9
|
+
export type ListViewProps<T> = PropsWithoutRef<FlashListProps<T>>
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* This is a Higher Order Component meant to ease the pain of using @shopify/flash-list
|
|
13
|
+
* when there is a chance that a user would have their device language set to an
|
|
14
|
+
* RTL language like Arabic or Persian. This component will use react-native's
|
|
15
|
+
* FlatList if the user's language is RTL or FlashList if the user's language is LTR.
|
|
16
|
+
*
|
|
17
|
+
* Because FlashList's props are a superset of FlatList's, you must pass estimatedItemSize
|
|
18
|
+
* to this component if you want to use it.
|
|
19
|
+
*
|
|
20
|
+
* This is a temporary workaround until the FlashList component supports RTL at
|
|
21
|
+
* which point this component can be removed and we will default to using FlashList everywhere.
|
|
22
|
+
* @see {@link https://github.com/Shopify/flash-list/issues/544|RTL Bug Android}
|
|
23
|
+
* @see {@link https://github.com/Shopify/flash-list/issues/840|Flashlist Not Support RTL}
|
|
24
|
+
* @param {FlashListProps | FlatListProps} props - The props for the `ListView` component.
|
|
25
|
+
* @param {RefObject<ListViewRef>} forwardRef - An optional forwarded ref.
|
|
26
|
+
* @returns {JSX.Element} The rendered `ListView` component.
|
|
27
|
+
*/
|
|
28
|
+
const ListViewComponent = forwardRef(
|
|
29
|
+
<T,>(props: ListViewProps<T>, ref: ForwardedRef<ListViewRef<T>>) => {
|
|
30
|
+
const ListComponentWrapper = isRTL ? FlatList : FlashList
|
|
31
|
+
|
|
32
|
+
return <ListComponentWrapper {...props} ref={ref} />
|
|
33
|
+
},
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
ListViewComponent.displayName = "ListView"
|
|
37
|
+
|
|
38
|
+
export const ListView = ListViewComponent as <T>(
|
|
39
|
+
props: ListViewProps<T> & {
|
|
40
|
+
ref?: RefObject<ListViewRef<T> | null>
|
|
41
|
+
},
|
|
42
|
+
) => ReactElement
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import { ReactNode, useRef, useState } from "react"
|
|
2
|
+
import {
|
|
3
|
+
KeyboardAvoidingView,
|
|
4
|
+
KeyboardAvoidingViewProps,
|
|
5
|
+
LayoutChangeEvent,
|
|
6
|
+
Platform,
|
|
7
|
+
ScrollView,
|
|
8
|
+
ScrollViewProps,
|
|
9
|
+
StyleProp,
|
|
10
|
+
View,
|
|
11
|
+
ViewStyle,
|
|
12
|
+
} from "react-native"
|
|
13
|
+
import { useScrollToTop } from "@react-navigation/native"
|
|
14
|
+
import { SystemBars, SystemBarsProps, SystemBarStyle } from "react-native-edge-to-edge"
|
|
15
|
+
import { KeyboardAwareScrollView } from "react-native-keyboard-controller"
|
|
16
|
+
|
|
17
|
+
import { useAppTheme } from "@/theme/context"
|
|
18
|
+
import { $styles } from "@/theme/styles"
|
|
19
|
+
import { ExtendedEdge, useSafeAreaInsetsStyle } from "@/utils/useSafeAreaInsetsStyle"
|
|
20
|
+
|
|
21
|
+
export const DEFAULT_BOTTOM_OFFSET = 50
|
|
22
|
+
|
|
23
|
+
interface BaseScreenProps {
|
|
24
|
+
/**
|
|
25
|
+
* Children components.
|
|
26
|
+
*/
|
|
27
|
+
children?: ReactNode
|
|
28
|
+
/**
|
|
29
|
+
* Style for the outer content container useful for padding & margin.
|
|
30
|
+
*/
|
|
31
|
+
style?: StyleProp<ViewStyle>
|
|
32
|
+
/**
|
|
33
|
+
* Style for the inner content container useful for padding & margin.
|
|
34
|
+
*/
|
|
35
|
+
contentContainerStyle?: StyleProp<ViewStyle>
|
|
36
|
+
/**
|
|
37
|
+
* Override the default edges for the safe area.
|
|
38
|
+
*/
|
|
39
|
+
safeAreaEdges?: ExtendedEdge[]
|
|
40
|
+
/**
|
|
41
|
+
* Background color
|
|
42
|
+
*/
|
|
43
|
+
backgroundColor?: string
|
|
44
|
+
/**
|
|
45
|
+
* System bar setting. Defaults to dark.
|
|
46
|
+
*/
|
|
47
|
+
systemBarStyle?: SystemBarStyle
|
|
48
|
+
/**
|
|
49
|
+
* By how much should we offset the keyboard? Defaults to 0.
|
|
50
|
+
*/
|
|
51
|
+
keyboardOffset?: number
|
|
52
|
+
/**
|
|
53
|
+
* By how much we scroll up when the keyboard is shown. Defaults to 50.
|
|
54
|
+
*/
|
|
55
|
+
keyboardBottomOffset?: number
|
|
56
|
+
/**
|
|
57
|
+
* Pass any additional props directly to the SystemBars component.
|
|
58
|
+
*/
|
|
59
|
+
SystemBarsProps?: SystemBarsProps
|
|
60
|
+
/**
|
|
61
|
+
* Pass any additional props directly to the KeyboardAvoidingView component.
|
|
62
|
+
*/
|
|
63
|
+
KeyboardAvoidingViewProps?: KeyboardAvoidingViewProps
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
interface FixedScreenProps extends BaseScreenProps {
|
|
67
|
+
preset?: "fixed"
|
|
68
|
+
}
|
|
69
|
+
interface ScrollScreenProps extends BaseScreenProps {
|
|
70
|
+
preset?: "scroll"
|
|
71
|
+
/**
|
|
72
|
+
* Should keyboard persist on screen tap. Defaults to handled.
|
|
73
|
+
* Only applies to scroll preset.
|
|
74
|
+
*/
|
|
75
|
+
keyboardShouldPersistTaps?: "handled" | "always" | "never"
|
|
76
|
+
/**
|
|
77
|
+
* Pass any additional props directly to the ScrollView component.
|
|
78
|
+
*/
|
|
79
|
+
ScrollViewProps?: ScrollViewProps
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
interface AutoScreenProps extends Omit<ScrollScreenProps, "preset"> {
|
|
83
|
+
preset?: "auto"
|
|
84
|
+
/**
|
|
85
|
+
* Threshold to trigger the automatic disabling/enabling of scroll ability.
|
|
86
|
+
* Defaults to `{ percent: 0.92 }`.
|
|
87
|
+
*/
|
|
88
|
+
scrollEnabledToggleThreshold?: { percent?: number; point?: number }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export type ScreenProps = ScrollScreenProps | FixedScreenProps | AutoScreenProps
|
|
92
|
+
|
|
93
|
+
const isIos = Platform.OS === "ios"
|
|
94
|
+
|
|
95
|
+
type ScreenPreset = "fixed" | "scroll" | "auto"
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @param {ScreenPreset?} preset - The preset to check.
|
|
99
|
+
* @returns {boolean} - Whether the preset is non-scrolling.
|
|
100
|
+
*/
|
|
101
|
+
function isNonScrolling(preset?: ScreenPreset) {
|
|
102
|
+
return !preset || preset === "fixed"
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Custom hook that handles the automatic enabling/disabling of scroll ability based on the content size and screen size.
|
|
107
|
+
* @param {UseAutoPresetProps} props - The props for the `useAutoPreset` hook.
|
|
108
|
+
* @returns {{boolean, Function, Function}} - The scroll state, and the `onContentSizeChange` and `onLayout` functions.
|
|
109
|
+
*/
|
|
110
|
+
function useAutoPreset(props: AutoScreenProps): {
|
|
111
|
+
scrollEnabled: boolean
|
|
112
|
+
onContentSizeChange: (w: number, h: number) => void
|
|
113
|
+
onLayout: (e: LayoutChangeEvent) => void
|
|
114
|
+
} {
|
|
115
|
+
const { preset, scrollEnabledToggleThreshold } = props
|
|
116
|
+
const { percent = 0.92, point = 0 } = scrollEnabledToggleThreshold || {}
|
|
117
|
+
|
|
118
|
+
const scrollViewHeight = useRef<null | number>(null)
|
|
119
|
+
const scrollViewContentHeight = useRef<null | number>(null)
|
|
120
|
+
const [scrollEnabled, setScrollEnabled] = useState(true)
|
|
121
|
+
|
|
122
|
+
function updateScrollState() {
|
|
123
|
+
if (scrollViewHeight.current === null || scrollViewContentHeight.current === null) return
|
|
124
|
+
|
|
125
|
+
// check whether content fits the screen then toggle scroll state according to it
|
|
126
|
+
const contentFitsScreen = (function () {
|
|
127
|
+
if (point) {
|
|
128
|
+
return scrollViewContentHeight.current < scrollViewHeight.current - point
|
|
129
|
+
} else {
|
|
130
|
+
return scrollViewContentHeight.current < scrollViewHeight.current * percent
|
|
131
|
+
}
|
|
132
|
+
})()
|
|
133
|
+
|
|
134
|
+
// content is less than the size of the screen, so we can disable scrolling
|
|
135
|
+
if (scrollEnabled && contentFitsScreen) setScrollEnabled(false)
|
|
136
|
+
|
|
137
|
+
// content is greater than the size of the screen, so let's enable scrolling
|
|
138
|
+
if (!scrollEnabled && !contentFitsScreen) setScrollEnabled(true)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* @param {number} w - The width of the content.
|
|
143
|
+
* @param {number} h - The height of the content.
|
|
144
|
+
*/
|
|
145
|
+
function onContentSizeChange(w: number, h: number) {
|
|
146
|
+
// update scroll-view content height
|
|
147
|
+
scrollViewContentHeight.current = h
|
|
148
|
+
updateScrollState()
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* @param {LayoutChangeEvent} e = The layout change event.
|
|
153
|
+
*/
|
|
154
|
+
function onLayout(e: LayoutChangeEvent) {
|
|
155
|
+
const { height } = e.nativeEvent.layout
|
|
156
|
+
// update scroll-view height
|
|
157
|
+
scrollViewHeight.current = height
|
|
158
|
+
updateScrollState()
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// update scroll state on every render
|
|
162
|
+
if (preset === "auto") updateScrollState()
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
scrollEnabled: preset === "auto" ? scrollEnabled : true,
|
|
166
|
+
onContentSizeChange,
|
|
167
|
+
onLayout,
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* @param {ScreenProps} props - The props for the `ScreenWithoutScrolling` component.
|
|
173
|
+
* @returns {JSX.Element} - The rendered `ScreenWithoutScrolling` component.
|
|
174
|
+
*/
|
|
175
|
+
function ScreenWithoutScrolling(props: ScreenProps) {
|
|
176
|
+
const { style, contentContainerStyle, children, preset } = props
|
|
177
|
+
return (
|
|
178
|
+
<View style={[$outerStyle, style]}>
|
|
179
|
+
<View style={[$innerStyle, preset === "fixed" && $justifyFlexEnd, contentContainerStyle]}>
|
|
180
|
+
{children}
|
|
181
|
+
</View>
|
|
182
|
+
</View>
|
|
183
|
+
)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* @param {ScreenProps} props - The props for the `ScreenWithScrolling` component.
|
|
188
|
+
* @returns {JSX.Element} - The rendered `ScreenWithScrolling` component.
|
|
189
|
+
*/
|
|
190
|
+
function ScreenWithScrolling(props: ScreenProps) {
|
|
191
|
+
const {
|
|
192
|
+
children,
|
|
193
|
+
keyboardShouldPersistTaps = "handled",
|
|
194
|
+
keyboardBottomOffset = DEFAULT_BOTTOM_OFFSET,
|
|
195
|
+
contentContainerStyle,
|
|
196
|
+
ScrollViewProps,
|
|
197
|
+
style,
|
|
198
|
+
} = props as ScrollScreenProps
|
|
199
|
+
|
|
200
|
+
const ref = useRef<ScrollView>(null)
|
|
201
|
+
|
|
202
|
+
const { scrollEnabled, onContentSizeChange, onLayout } = useAutoPreset(props as AutoScreenProps)
|
|
203
|
+
|
|
204
|
+
// Add native behavior of pressing the active tab to scroll to the top of the content
|
|
205
|
+
// More info at: https://reactnavigation.org/docs/use-scroll-to-top/
|
|
206
|
+
useScrollToTop(ref)
|
|
207
|
+
|
|
208
|
+
return (
|
|
209
|
+
<KeyboardAwareScrollView
|
|
210
|
+
bottomOffset={keyboardBottomOffset}
|
|
211
|
+
{...{ keyboardShouldPersistTaps, scrollEnabled, ref }}
|
|
212
|
+
{...ScrollViewProps}
|
|
213
|
+
onLayout={(e) => {
|
|
214
|
+
onLayout(e)
|
|
215
|
+
ScrollViewProps?.onLayout?.(e)
|
|
216
|
+
}}
|
|
217
|
+
onContentSizeChange={(w: number, h: number) => {
|
|
218
|
+
onContentSizeChange(w, h)
|
|
219
|
+
ScrollViewProps?.onContentSizeChange?.(w, h)
|
|
220
|
+
}}
|
|
221
|
+
style={[$outerStyle, ScrollViewProps?.style, style]}
|
|
222
|
+
contentContainerStyle={[
|
|
223
|
+
$innerStyle,
|
|
224
|
+
ScrollViewProps?.contentContainerStyle,
|
|
225
|
+
contentContainerStyle,
|
|
226
|
+
]}
|
|
227
|
+
>
|
|
228
|
+
{children}
|
|
229
|
+
</KeyboardAwareScrollView>
|
|
230
|
+
)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Represents a screen component that provides a consistent layout and behavior for different screen presets.
|
|
235
|
+
* The `Screen` component can be used with different presets such as "fixed", "scroll", or "auto".
|
|
236
|
+
* It handles safe area insets, status bar settings, keyboard avoiding behavior, and scrollability based on the preset.
|
|
237
|
+
* @see [Documentation and Examples]{@link https://docs.infinite.red/ignite-cli/boilerplate/app/components/Screen/}
|
|
238
|
+
* @param {ScreenProps} props - The props for the `Screen` component.
|
|
239
|
+
* @returns {JSX.Element} The rendered `Screen` component.
|
|
240
|
+
*/
|
|
241
|
+
export function Screen(props: ScreenProps) {
|
|
242
|
+
const {
|
|
243
|
+
theme: { colors },
|
|
244
|
+
themeContext,
|
|
245
|
+
} = useAppTheme()
|
|
246
|
+
const {
|
|
247
|
+
backgroundColor,
|
|
248
|
+
KeyboardAvoidingViewProps,
|
|
249
|
+
keyboardOffset = 0,
|
|
250
|
+
safeAreaEdges,
|
|
251
|
+
SystemBarsProps,
|
|
252
|
+
systemBarStyle,
|
|
253
|
+
} = props
|
|
254
|
+
|
|
255
|
+
const $containerInsets = useSafeAreaInsetsStyle(safeAreaEdges)
|
|
256
|
+
|
|
257
|
+
return (
|
|
258
|
+
<View
|
|
259
|
+
style={[
|
|
260
|
+
$containerStyle,
|
|
261
|
+
{ backgroundColor: backgroundColor || colors.background },
|
|
262
|
+
$containerInsets,
|
|
263
|
+
]}
|
|
264
|
+
>
|
|
265
|
+
<SystemBars
|
|
266
|
+
style={systemBarStyle || (themeContext === "dark" ? "light" : "dark")}
|
|
267
|
+
{...SystemBarsProps}
|
|
268
|
+
/>
|
|
269
|
+
|
|
270
|
+
<KeyboardAvoidingView
|
|
271
|
+
behavior={isIos ? "padding" : "height"}
|
|
272
|
+
keyboardVerticalOffset={keyboardOffset}
|
|
273
|
+
{...KeyboardAvoidingViewProps}
|
|
274
|
+
style={[$styles.flex1, KeyboardAvoidingViewProps?.style]}
|
|
275
|
+
>
|
|
276
|
+
{isNonScrolling(props.preset) ? (
|
|
277
|
+
<ScreenWithoutScrolling {...props} />
|
|
278
|
+
) : (
|
|
279
|
+
<ScreenWithScrolling {...props} />
|
|
280
|
+
)}
|
|
281
|
+
</KeyboardAvoidingView>
|
|
282
|
+
</View>
|
|
283
|
+
)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const $containerStyle: ViewStyle = {
|
|
287
|
+
flex: 1,
|
|
288
|
+
height: "100%",
|
|
289
|
+
width: "100%",
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const $outerStyle: ViewStyle = {
|
|
293
|
+
flex: 1,
|
|
294
|
+
height: "100%",
|
|
295
|
+
width: "100%",
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const $justifyFlexEnd: ViewStyle = {
|
|
299
|
+
justifyContent: "flex-end",
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const $innerStyle: ViewStyle = {
|
|
303
|
+
justifyContent: "flex-start",
|
|
304
|
+
alignItems: "stretch",
|
|
305
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { NavigationContainer } from "@react-navigation/native"
|
|
2
|
+
import { render } from "@testing-library/react-native"
|
|
3
|
+
|
|
4
|
+
import { Text } from "./Text"
|
|
5
|
+
import { ThemeProvider } from "../theme/context"
|
|
6
|
+
|
|
7
|
+
/* This is an example component test using react-native-testing-library. For more
|
|
8
|
+
* information on how to write your own, see the documentation here:
|
|
9
|
+
* https://callstack.github.io/react-native-testing-library/ */
|
|
10
|
+
const testText = "Test string"
|
|
11
|
+
|
|
12
|
+
describe("Text", () => {
|
|
13
|
+
it("should render the component", () => {
|
|
14
|
+
const { getByText } = render(
|
|
15
|
+
<ThemeProvider>
|
|
16
|
+
<NavigationContainer>
|
|
17
|
+
<Text text={testText} />
|
|
18
|
+
</NavigationContainer>
|
|
19
|
+
</ThemeProvider>,
|
|
20
|
+
)
|
|
21
|
+
expect(getByText(testText)).toBeDefined()
|
|
22
|
+
})
|
|
23
|
+
})
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { ReactNode, forwardRef, ForwardedRef } from "react"
|
|
2
|
+
// eslint-disable-next-line no-restricted-imports
|
|
3
|
+
import { StyleProp, Text as RNText, TextProps as RNTextProps, TextStyle } from "react-native"
|
|
4
|
+
import { TOptions } from "i18next"
|
|
5
|
+
|
|
6
|
+
import { isRTL, TxKeyPath } from "@/i18n"
|
|
7
|
+
import { translate } from "@/i18n/translate"
|
|
8
|
+
import type { ThemedStyle, ThemedStyleArray } from "@/theme/types"
|
|
9
|
+
import { useAppTheme } from "@/theme/context"
|
|
10
|
+
import { typography } from "@/theme/typography"
|
|
11
|
+
|
|
12
|
+
type Sizes = keyof typeof $sizeStyles
|
|
13
|
+
type Weights = keyof typeof typography.primary
|
|
14
|
+
type Presets = "default" | "bold" | "heading" | "subheading" | "formLabel" | "formHelper"
|
|
15
|
+
|
|
16
|
+
export interface TextProps extends RNTextProps {
|
|
17
|
+
/**
|
|
18
|
+
* Text which is looked up via i18n.
|
|
19
|
+
*/
|
|
20
|
+
tx?: TxKeyPath
|
|
21
|
+
/**
|
|
22
|
+
* The text to display if not using `tx` or nested components.
|
|
23
|
+
*/
|
|
24
|
+
text?: string
|
|
25
|
+
/**
|
|
26
|
+
* Optional options to pass to i18n. Useful for interpolation
|
|
27
|
+
* as well as explicitly setting locale or translation fallbacks.
|
|
28
|
+
*/
|
|
29
|
+
txOptions?: TOptions
|
|
30
|
+
/**
|
|
31
|
+
* An optional style override useful for padding & margin.
|
|
32
|
+
*/
|
|
33
|
+
style?: StyleProp<TextStyle>
|
|
34
|
+
/**
|
|
35
|
+
* One of the different types of text presets.
|
|
36
|
+
*/
|
|
37
|
+
preset?: Presets
|
|
38
|
+
/**
|
|
39
|
+
* Text weight modifier.
|
|
40
|
+
*/
|
|
41
|
+
weight?: Weights
|
|
42
|
+
/**
|
|
43
|
+
* Text size modifier.
|
|
44
|
+
*/
|
|
45
|
+
size?: Sizes
|
|
46
|
+
/**
|
|
47
|
+
* Children components.
|
|
48
|
+
*/
|
|
49
|
+
children?: ReactNode
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* For your text displaying needs.
|
|
54
|
+
* This component is a HOC over the built-in React Native one.
|
|
55
|
+
* @see [Documentation and Examples]{@link https://docs.infinite.red/ignite-cli/boilerplate/app/components/Text/}
|
|
56
|
+
* @param {TextProps} props - The props for the `Text` component.
|
|
57
|
+
* @returns {JSX.Element} The rendered `Text` component.
|
|
58
|
+
*/
|
|
59
|
+
export const Text = forwardRef(function Text(props: TextProps, ref: ForwardedRef<RNText>) {
|
|
60
|
+
const { weight, size, tx, txOptions, text, children, style: $styleOverride, ...rest } = props
|
|
61
|
+
const { themed } = useAppTheme()
|
|
62
|
+
|
|
63
|
+
const i18nText = tx && translate(tx, txOptions)
|
|
64
|
+
const content = i18nText || text || children
|
|
65
|
+
|
|
66
|
+
const preset: Presets = props.preset ?? "default"
|
|
67
|
+
const $styles: StyleProp<TextStyle> = [
|
|
68
|
+
$rtlStyle,
|
|
69
|
+
themed($presets[preset]),
|
|
70
|
+
weight && $fontWeightStyles[weight],
|
|
71
|
+
size && $sizeStyles[size],
|
|
72
|
+
$styleOverride,
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<RNText {...rest} style={$styles} ref={ref}>
|
|
77
|
+
{content}
|
|
78
|
+
</RNText>
|
|
79
|
+
)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
const $sizeStyles = {
|
|
83
|
+
xxl: { fontSize: 36, lineHeight: 44 } satisfies TextStyle,
|
|
84
|
+
xl: { fontSize: 24, lineHeight: 34 } satisfies TextStyle,
|
|
85
|
+
lg: { fontSize: 20, lineHeight: 32 } satisfies TextStyle,
|
|
86
|
+
md: { fontSize: 18, lineHeight: 26 } satisfies TextStyle,
|
|
87
|
+
sm: { fontSize: 16, lineHeight: 24 } satisfies TextStyle,
|
|
88
|
+
xs: { fontSize: 14, lineHeight: 21 } satisfies TextStyle,
|
|
89
|
+
xxs: { fontSize: 12, lineHeight: 18 } satisfies TextStyle,
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const $fontWeightStyles = Object.entries(typography.primary).reduce((acc, [weight, fontFamily]) => {
|
|
93
|
+
return { ...acc, [weight]: { fontFamily } }
|
|
94
|
+
}, {}) as Record<Weights, TextStyle>
|
|
95
|
+
|
|
96
|
+
const $baseStyle: ThemedStyle<TextStyle> = (theme) => ({
|
|
97
|
+
...$sizeStyles.sm,
|
|
98
|
+
...$fontWeightStyles.normal,
|
|
99
|
+
color: theme.colors.text,
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
const $presets: Record<Presets, ThemedStyleArray<TextStyle>> = {
|
|
103
|
+
default: [$baseStyle],
|
|
104
|
+
bold: [$baseStyle, { ...$fontWeightStyles.bold }],
|
|
105
|
+
heading: [
|
|
106
|
+
$baseStyle,
|
|
107
|
+
{
|
|
108
|
+
...$sizeStyles.xxl,
|
|
109
|
+
...$fontWeightStyles.bold,
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
subheading: [$baseStyle, { ...$sizeStyles.lg, ...$fontWeightStyles.medium }],
|
|
113
|
+
formLabel: [$baseStyle, { ...$fontWeightStyles.medium }],
|
|
114
|
+
formHelper: [$baseStyle, { ...$sizeStyles.sm, ...$fontWeightStyles.normal }],
|
|
115
|
+
}
|
|
116
|
+
const $rtlStyle: TextStyle = isRTL ? { writingDirection: "rtl" } : {}
|