@bigbinary/neetoui-rn 2.3.4 → 3.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/README.md +118 -0
- package/lib/typescript/src/components/Alert/Alert.d.ts +32 -0
- package/lib/typescript/src/components/Alert/Alert.d.ts.map +1 -0
- package/lib/typescript/src/components/Alert/index.d.ts +2 -0
- package/lib/typescript/src/components/Alert/index.d.ts.map +1 -0
- package/lib/typescript/src/components/Avatar/Avatar.d.ts +21 -0
- package/lib/typescript/src/components/Avatar/Avatar.d.ts.map +1 -0
- package/lib/typescript/src/components/Avatar/index.d.ts +2 -0
- package/lib/typescript/src/components/Avatar/index.d.ts.map +1 -0
- package/lib/typescript/src/components/BottomSheet/BottomSheet.d.ts +61 -0
- package/lib/typescript/src/components/BottomSheet/BottomSheet.d.ts.map +1 -0
- package/lib/typescript/src/components/BottomSheet/index.d.ts +2 -0
- package/lib/typescript/src/components/BottomSheet/index.d.ts.map +1 -0
- package/lib/typescript/src/components/BottomSheetMenu/BottomSheetMenu.d.ts +28 -0
- package/lib/typescript/src/components/BottomSheetMenu/BottomSheetMenu.d.ts.map +1 -0
- package/lib/typescript/src/components/BottomSheetMenu/index.d.ts +2 -0
- package/lib/typescript/src/components/BottomSheetMenu/index.d.ts.map +1 -0
- package/lib/typescript/src/components/Button/Button.d.ts +79 -0
- package/lib/typescript/src/components/Button/Button.d.ts.map +1 -0
- package/lib/typescript/src/components/Button/index.d.ts +2 -0
- package/lib/typescript/src/components/Button/index.d.ts.map +1 -0
- package/lib/typescript/src/components/ChatInput/AttachmentsView.d.ts +11 -0
- package/lib/typescript/src/components/ChatInput/AttachmentsView.d.ts.map +1 -0
- package/lib/typescript/src/components/ChatInput/ChatInput.d.ts +78 -0
- package/lib/typescript/src/components/ChatInput/ChatInput.d.ts.map +1 -0
- package/lib/typescript/src/components/ChatInput/IconButton.d.ts +16 -0
- package/lib/typescript/src/components/ChatInput/IconButton.d.ts.map +1 -0
- package/lib/typescript/src/components/ChatInput/MentionsInput.d.ts +13 -0
- package/lib/typescript/src/components/ChatInput/MentionsInput.d.ts.map +1 -0
- package/lib/typescript/src/components/ChatInput/constants.d.ts +9 -0
- package/lib/typescript/src/components/ChatInput/constants.d.ts.map +1 -0
- package/lib/typescript/src/components/ChatInput/index.d.ts +4 -0
- package/lib/typescript/src/components/ChatInput/index.d.ts.map +1 -0
- package/lib/typescript/src/components/ChatInput/mentions.d.ts +26 -0
- package/lib/typescript/src/components/ChatInput/mentions.d.ts.map +1 -0
- package/lib/typescript/src/components/CheckBox/CheckBox.d.ts +13 -0
- package/lib/typescript/src/components/CheckBox/CheckBox.d.ts.map +1 -0
- package/lib/typescript/src/components/CheckBox/index.d.ts +2 -0
- package/lib/typescript/src/components/CheckBox/index.d.ts.map +1 -0
- package/lib/typescript/src/components/Container/Container.d.ts +9 -0
- package/lib/typescript/src/components/Container/Container.d.ts.map +1 -0
- package/lib/typescript/src/components/Container/index.d.ts +2 -0
- package/lib/typescript/src/components/Container/index.d.ts.map +1 -0
- package/lib/typescript/src/components/Divider/Divider.d.ts +7 -0
- package/lib/typescript/src/components/Divider/Divider.d.ts.map +1 -0
- package/lib/typescript/src/components/Divider/index.d.ts +2 -0
- package/lib/typescript/src/components/Divider/index.d.ts.map +1 -0
- package/lib/typescript/src/components/EmptyView/EmptyView.d.ts +26 -0
- package/lib/typescript/src/components/EmptyView/EmptyView.d.ts.map +1 -0
- package/lib/typescript/src/components/EmptyView/index.d.ts +2 -0
- package/lib/typescript/src/components/EmptyView/index.d.ts.map +1 -0
- package/lib/typescript/src/components/FlashList/FlashList.d.ts +14 -0
- package/lib/typescript/src/components/FlashList/FlashList.d.ts.map +1 -0
- package/lib/typescript/src/components/FlashList/index.d.ts +2 -0
- package/lib/typescript/src/components/FlashList/index.d.ts.map +1 -0
- package/lib/typescript/src/components/Header/BaseHeader.d.ts +5 -0
- package/lib/typescript/src/components/Header/BaseHeader.d.ts.map +1 -0
- package/lib/typescript/src/components/Header/Header.d.ts +21 -0
- package/lib/typescript/src/components/Header/Header.d.ts.map +1 -0
- package/lib/typescript/src/components/Header/ScreenHeader.d.ts +11 -0
- package/lib/typescript/src/components/Header/ScreenHeader.d.ts.map +1 -0
- package/lib/typescript/src/components/Header/index.d.ts +4 -0
- package/lib/typescript/src/components/Header/index.d.ts.map +1 -0
- package/lib/typescript/src/components/Input/Input.d.ts +15 -0
- package/lib/typescript/src/components/Input/Input.d.ts.map +1 -0
- package/lib/typescript/src/components/Input/index.d.ts +2 -0
- package/lib/typescript/src/components/Input/index.d.ts.map +1 -0
- package/lib/typescript/src/components/Loader/Loader.d.ts +12 -0
- package/lib/typescript/src/components/Loader/Loader.d.ts.map +1 -0
- package/lib/typescript/src/components/Loader/index.d.ts +2 -0
- package/lib/typescript/src/components/Loader/index.d.ts.map +1 -0
- package/lib/typescript/src/components/NeetoUIProvider/NeetoUIProvider.d.ts +16 -0
- package/lib/typescript/src/components/NeetoUIProvider/NeetoUIProvider.d.ts.map +1 -0
- package/lib/typescript/src/components/NeetoUIProvider/index.d.ts +2 -0
- package/lib/typescript/src/components/NeetoUIProvider/index.d.ts.map +1 -0
- package/lib/typescript/src/components/OnBoarding/OnBoarding.d.ts +22 -0
- package/lib/typescript/src/components/OnBoarding/OnBoarding.d.ts.map +1 -0
- package/lib/typescript/src/components/OnBoarding/index.d.ts +2 -0
- package/lib/typescript/src/components/OnBoarding/index.d.ts.map +1 -0
- package/lib/typescript/src/components/ParentView/ParentView.d.ts +13 -0
- package/lib/typescript/src/components/ParentView/ParentView.d.ts.map +1 -0
- package/lib/typescript/src/components/ParentView/index.d.ts +2 -0
- package/lib/typescript/src/components/ParentView/index.d.ts.map +1 -0
- package/lib/typescript/src/components/RadioButton/RadioButton.d.ts +12 -0
- package/lib/typescript/src/components/RadioButton/RadioButton.d.ts.map +1 -0
- package/lib/typescript/src/components/RadioButton/index.d.ts +2 -0
- package/lib/typescript/src/components/RadioButton/index.d.ts.map +1 -0
- package/lib/typescript/src/components/ScrollView/ScrollView.d.ts +8 -0
- package/lib/typescript/src/components/ScrollView/ScrollView.d.ts.map +1 -0
- package/lib/typescript/src/components/ScrollView/index.d.ts +2 -0
- package/lib/typescript/src/components/ScrollView/index.d.ts.map +1 -0
- package/lib/typescript/src/components/SearchBar/SearchBar.d.ts +12 -0
- package/lib/typescript/src/components/SearchBar/SearchBar.d.ts.map +1 -0
- package/lib/typescript/src/components/SearchBar/index.d.ts +2 -0
- package/lib/typescript/src/components/SearchBar/index.d.ts.map +1 -0
- package/lib/typescript/src/components/Skeleton/Skeleton.d.ts +41 -0
- package/lib/typescript/src/components/Skeleton/Skeleton.d.ts.map +1 -0
- package/lib/typescript/src/components/Skeleton/index.d.ts +2 -0
- package/lib/typescript/src/components/Skeleton/index.d.ts.map +1 -0
- package/lib/typescript/src/components/TextWithUrl/TextWithUrl.d.ts +11 -0
- package/lib/typescript/src/components/TextWithUrl/TextWithUrl.d.ts.map +1 -0
- package/lib/typescript/src/components/TextWithUrl/index.d.ts +2 -0
- package/lib/typescript/src/components/TextWithUrl/index.d.ts.map +1 -0
- package/lib/typescript/src/components/Toast/Toast.d.ts +13 -0
- package/lib/typescript/src/components/Toast/Toast.d.ts.map +1 -0
- package/lib/typescript/src/components/Toast/ToastGlyph.d.ts +4 -0
- package/lib/typescript/src/components/Toast/ToastGlyph.d.ts.map +1 -0
- package/lib/typescript/src/components/Toast/index.d.ts +2 -0
- package/lib/typescript/src/components/Toast/index.d.ts.map +1 -0
- package/lib/typescript/src/components/Touchable/Touchable.d.ts +23 -0
- package/lib/typescript/src/components/Touchable/Touchable.d.ts.map +1 -0
- package/lib/typescript/src/components/Touchable/index.d.ts +2 -0
- package/lib/typescript/src/components/Touchable/index.d.ts.map +1 -0
- package/lib/typescript/src/components/Typography/Typography.d.ts +64 -0
- package/lib/typescript/src/components/Typography/Typography.d.ts.map +1 -0
- package/lib/typescript/src/components/Typography/index.d.ts +2 -0
- package/lib/typescript/src/components/Typography/index.d.ts.map +1 -0
- package/lib/typescript/src/hooks/index.d.ts +4 -0
- package/lib/typescript/src/hooks/index.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useDebounce.d.ts +2 -0
- package/lib/typescript/src/hooks/useDebounce.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useNeetoTheme.d.ts +8 -0
- package/lib/typescript/src/hooks/useNeetoTheme.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useRefreshByUser.d.ts +5 -0
- package/lib/typescript/src/hooks/useRefreshByUser.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +31 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/theme/palette.d.ts +28 -0
- package/lib/typescript/src/theme/palette.d.ts.map +1 -0
- package/lib/typescript/src/theme/palette.generated.d.ts +134 -0
- package/lib/typescript/src/theme/palette.generated.d.ts.map +1 -0
- package/lib/typescript/src/theme/tokens.d.ts +184 -0
- package/lib/typescript/src/theme/tokens.d.ts.map +1 -0
- package/lib/typescript/src/unistyles-init.d.ts +2 -0
- package/lib/typescript/src/unistyles-init.d.ts.map +1 -0
- package/lib/typescript/src/unistyles.d.ts +21 -0
- package/lib/typescript/src/unistyles.d.ts.map +1 -0
- package/lib/typescript/src/utils/index.d.ts +18 -0
- package/lib/typescript/src/utils/index.d.ts.map +1 -0
- package/lib/typescript/src/utils/scale.d.ts +2 -0
- package/lib/typescript/src/utils/scale.d.ts.map +1 -0
- package/package.json +125 -51
- package/src/components/Alert/Alert.tsx +222 -0
- package/src/components/Alert/index.ts +1 -0
- package/src/components/Avatar/Avatar.tsx +123 -0
- package/src/components/Avatar/index.ts +1 -0
- package/src/components/BottomSheet/BottomSheet.tsx +468 -0
- package/src/components/BottomSheet/index.ts +5 -0
- package/src/components/BottomSheetMenu/BottomSheetMenu.tsx +150 -0
- package/src/components/BottomSheetMenu/index.ts +5 -0
- package/src/components/Button/Button.tsx +134 -0
- package/src/components/Button/index.ts +1 -0
- package/src/components/ChatInput/AttachmentsView.tsx +68 -0
- package/src/components/ChatInput/ChatInput.tsx +393 -0
- package/src/components/ChatInput/IconButton.tsx +52 -0
- package/src/components/ChatInput/MentionsInput.tsx +126 -0
- package/src/components/ChatInput/constants.ts +18 -0
- package/src/components/ChatInput/index.ts +11 -0
- package/src/components/ChatInput/mentions.ts +91 -0
- package/src/components/CheckBox/CheckBox.tsx +86 -0
- package/src/components/CheckBox/index.ts +1 -0
- package/src/components/Container/Container.tsx +14 -0
- package/src/components/Container/index.ts +1 -0
- package/src/components/Divider/Divider.tsx +32 -0
- package/src/components/Divider/index.ts +1 -0
- package/src/components/EmptyView/EmptyView.tsx +80 -0
- package/src/components/EmptyView/index.ts +1 -0
- package/src/components/FlashList/FlashList.tsx +162 -0
- package/src/components/FlashList/index.ts +1 -0
- package/src/components/Header/BaseHeader.tsx +19 -0
- package/src/components/Header/Header.tsx +114 -0
- package/src/components/Header/ScreenHeader.tsx +34 -0
- package/src/components/Header/index.ts +3 -0
- package/src/components/Input/Input.tsx +122 -0
- package/src/components/Input/index.ts +1 -0
- package/src/components/Loader/Loader.tsx +93 -0
- package/src/components/Loader/index.ts +1 -0
- package/src/components/NeetoUIProvider/NeetoUIProvider.tsx +43 -0
- package/src/components/NeetoUIProvider/index.ts +1 -0
- package/src/components/OnBoarding/OnBoarding.tsx +177 -0
- package/src/components/OnBoarding/index.ts +5 -0
- package/src/components/ParentView/ParentView.tsx +98 -0
- package/src/components/ParentView/index.ts +1 -0
- package/src/components/RadioButton/RadioButton.tsx +80 -0
- package/src/components/RadioButton/index.ts +1 -0
- package/src/components/ScrollView/ScrollView.tsx +30 -0
- package/src/components/ScrollView/index.ts +1 -0
- package/src/components/SearchBar/SearchBar.tsx +122 -0
- package/src/components/SearchBar/index.ts +1 -0
- package/src/components/Skeleton/Skeleton.tsx +121 -0
- package/src/components/Skeleton/index.ts +1 -0
- package/src/components/TextWithUrl/TextWithUrl.tsx +54 -0
- package/src/components/TextWithUrl/index.ts +1 -0
- package/src/components/Toast/Toast.tsx +157 -0
- package/src/components/Toast/ToastGlyph.tsx +53 -0
- package/src/components/Toast/index.ts +1 -0
- package/src/components/Touchable/Touchable.tsx +54 -0
- package/src/components/Touchable/index.ts +1 -0
- package/src/components/Typography/Typography.tsx +76 -0
- package/src/components/Typography/index.ts +1 -0
- package/src/hooks/index.ts +3 -0
- package/src/hooks/useDebounce.ts +17 -0
- package/src/hooks/useNeetoTheme.ts +10 -0
- package/src/hooks/useRefreshByUser.ts +17 -0
- package/src/index.ts +68 -0
- package/src/theme/palette.generated.ts +141 -0
- package/src/theme/palette.ts +34 -0
- package/src/theme/tokens.ts +205 -0
- package/src/types/neeto-icons-rn.d.ts +33 -0
- package/src/unistyles-init.ts +16 -0
- package/src/unistyles.ts +34 -0
- package/src/utils/index.ts +41 -0
- package/src/utils/scale.ts +16 -0
- package/components/Accordion.js +0 -1
- package/components/ActionIcon.js +0 -1
- package/components/Alert.js +0 -1
- package/components/Avatar.js +0 -1
- package/components/Badge.js +0 -1
- package/components/BottomSheet.js +0 -1
- package/components/BottomTabBar.js +0 -1
- package/components/Button.js +0 -1
- package/components/ButtonGroup.js +0 -1
- package/components/Calendar.js +0 -1
- package/components/Card.js +0 -1
- package/components/Carousel.js +0 -1
- package/components/ChatInput/AttachmentsView.js +0 -1
- package/components/ChatInput/Badge.js +0 -1
- package/components/ChatInput/ChatInput.js +0 -1
- package/components/ChatInput/EmailFields.js +0 -1
- package/components/ChatInput/IconButton.js +0 -1
- package/components/ChatInput/MentionsInputWrapper.js +0 -1
- package/components/CheckBox.js +0 -1
- package/components/Chip.js +0 -1
- package/components/Container.js +0 -1
- package/components/Divider.js +0 -1
- package/components/FAB.js +0 -1
- package/components/FlashList.js +0 -1
- package/components/FlatList.js +0 -1
- package/components/Input.js +0 -1
- package/components/InputEmailChip.js +0 -1
- package/components/LineLoader.js +0 -1
- package/components/ListItem.js +0 -1
- package/components/Loader.js +0 -1
- package/components/MultiSelect.js +0 -1
- package/components/NotificationIcon.js +0 -1
- package/components/NotificationPreferenceList.js +0 -1
- package/components/OnBoarding.js +0 -1
- package/components/OrganizationItem.js +0 -1
- package/components/OtpInputs.js +0 -1
- package/components/ParentView.js +0 -1
- package/components/Popover.js +0 -1
- package/components/RadioButton.js +0 -1
- package/components/RichTextEditor.js +0 -1
- package/components/Ripple.js +0 -1
- package/components/ScrollView.js +0 -1
- package/components/SearchBar.js +0 -1
- package/components/SegmentedTopBar/Indicator.js +0 -1
- package/components/SegmentedTopBar/Tab.js +0 -1
- package/components/SegmentedTopBar/index.js +0 -1
- package/components/SocialButton.js +0 -1
- package/components/Toast.js +0 -1
- package/components/ToggleSwitch.js +0 -1
- package/components/TopBar.js +0 -1
- package/components/Touchable.js +0 -1
- package/components/TouchableWithoutFeedback.js +0 -1
- package/components/Typography.js +0 -1
- package/components/index.js +0 -1
- package/config/index.js +0 -1
- package/config/toasterConfig.js +0 -1
- package/contexts/index.js +0 -1
- package/hooks/index.js +0 -1
- package/hooks/useDebounce.js +0 -1
- package/hooks/useKeyboard.js +0 -1
- package/hooks/useRefreshByUser.js +0 -1
- package/index.d.ts +0 -630
- package/index.js +0 -1
- package/react-native.config.js +0 -10
- package/theme/index.js +0 -1
- package/utils/index.js +0 -1
- package/utils/scale.js +0 -1
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createElement,
|
|
3
|
+
isValidElement,
|
|
4
|
+
type ComponentType,
|
|
5
|
+
type ReactNode,
|
|
6
|
+
} from "react";
|
|
7
|
+
import { View } from "react-native";
|
|
8
|
+
import { LeftArrow } from "@bigbinary/neeto-icons-rn";
|
|
9
|
+
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
|
10
|
+
|
|
11
|
+
import { moderateScale } from "../../utils/scale";
|
|
12
|
+
import { Skeleton } from "../Skeleton";
|
|
13
|
+
import { Touchable } from "../Touchable";
|
|
14
|
+
import { Typography } from "../Typography";
|
|
15
|
+
import { BaseHeader } from "./BaseHeader";
|
|
16
|
+
|
|
17
|
+
export type HeaderProps = {
|
|
18
|
+
title: string;
|
|
19
|
+
subTitle?: string;
|
|
20
|
+
/** Back chevron renders only when this is passed. */
|
|
21
|
+
onBack?: () => void;
|
|
22
|
+
/** Leading slot after the back chevron (menu button, workspace logo…). */
|
|
23
|
+
LeftSlot?: ComponentType | ReactNode;
|
|
24
|
+
/** Trailing slot (actions, overflow menus…). */
|
|
25
|
+
RightSlot?: ComponentType | ReactNode;
|
|
26
|
+
/** Shows the progress line under the header (v2 LineLoader spot). */
|
|
27
|
+
isLoading?: boolean;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// memo() and forwardRef() components are objects, not functions — rendering
|
|
31
|
+
// one as a child throws "Objects are not valid as a React child", so the
|
|
32
|
+
// slot has to be instantiated whenever it is a component type of any kind.
|
|
33
|
+
const isComponentType = (slot: unknown): slot is ComponentType =>
|
|
34
|
+
typeof slot === "function" ||
|
|
35
|
+
(typeof slot === "object" &&
|
|
36
|
+
slot !== null &&
|
|
37
|
+
"$$typeof" in slot &&
|
|
38
|
+
!isValidElement(slot));
|
|
39
|
+
|
|
40
|
+
const renderSlot = (Slot: ComponentType | ReactNode): ReactNode => {
|
|
41
|
+
if (Slot === null || Slot === undefined) return null;
|
|
42
|
+
|
|
43
|
+
return isComponentType(Slot) ? createElement(Slot) : Slot;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Navigation header (promoted from neeto-commons-rn). The commons version
|
|
48
|
+
* read useNavigation() itself; v3 is navigation-agnostic — apps (or the
|
|
49
|
+
* commons compat wrapper) pass onBack and fill the slots. The mark-all
|
|
50
|
+
* menu, hamburger and workspace logo become slot content.
|
|
51
|
+
*/
|
|
52
|
+
export const Header = ({
|
|
53
|
+
title,
|
|
54
|
+
subTitle,
|
|
55
|
+
onBack,
|
|
56
|
+
LeftSlot,
|
|
57
|
+
RightSlot,
|
|
58
|
+
isLoading = false,
|
|
59
|
+
}: HeaderProps) => {
|
|
60
|
+
const { theme } = useUnistyles();
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<View>
|
|
64
|
+
<BaseHeader style={styles.container}>
|
|
65
|
+
{onBack && (
|
|
66
|
+
<Touchable
|
|
67
|
+
accessibilityLabel="Go back"
|
|
68
|
+
accessibilityRole="button"
|
|
69
|
+
hitSlop={moderateScale(8)}
|
|
70
|
+
style={styles.backButton}
|
|
71
|
+
onPress={onBack}
|
|
72
|
+
>
|
|
73
|
+
<LeftArrow
|
|
74
|
+
color={theme.colors.text.primary}
|
|
75
|
+
size={moderateScale(28)}
|
|
76
|
+
/>
|
|
77
|
+
</Touchable>
|
|
78
|
+
)}
|
|
79
|
+
{renderSlot(LeftSlot)}
|
|
80
|
+
<View style={styles.titleBlock}>
|
|
81
|
+
<Typography numberOfLines={1} size="xl" weight="semibold">
|
|
82
|
+
{title}
|
|
83
|
+
</Typography>
|
|
84
|
+
{!!subTitle && (
|
|
85
|
+
<Typography numberOfLines={1} size="2xs" style={styles.subTitle}>
|
|
86
|
+
{subTitle}
|
|
87
|
+
</Typography>
|
|
88
|
+
)}
|
|
89
|
+
</View>
|
|
90
|
+
{renderSlot(RightSlot)}
|
|
91
|
+
</BaseHeader>
|
|
92
|
+
{isLoading && (
|
|
93
|
+
<Skeleton animated height={moderateScale(2)} variant="line" />
|
|
94
|
+
)}
|
|
95
|
+
</View>
|
|
96
|
+
);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const styles = StyleSheet.create(theme => ({
|
|
100
|
+
container: {
|
|
101
|
+
gap: moderateScale(8),
|
|
102
|
+
minHeight: moderateScale(48),
|
|
103
|
+
paddingHorizontal: moderateScale(16),
|
|
104
|
+
},
|
|
105
|
+
backButton: {
|
|
106
|
+
marginLeft: moderateScale(-8),
|
|
107
|
+
},
|
|
108
|
+
titleBlock: {
|
|
109
|
+
flex: 1,
|
|
110
|
+
},
|
|
111
|
+
subTitle: {
|
|
112
|
+
color: theme.colors.text.muted,
|
|
113
|
+
},
|
|
114
|
+
}));
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Platform, type StyleProp, type ViewStyle } from "react-native";
|
|
2
|
+
import { StyleSheet } from "react-native-unistyles";
|
|
3
|
+
|
|
4
|
+
import { moderateScale } from "../../utils/scale";
|
|
5
|
+
import { Typography } from "../Typography";
|
|
6
|
+
import { BaseHeader } from "./BaseHeader";
|
|
7
|
+
|
|
8
|
+
export type ScreenHeaderProps = {
|
|
9
|
+
title: string;
|
|
10
|
+
style?: StyleProp<ViewStyle>;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Large screen title header (promoted from neeto-commons-rn). Platform
|
|
15
|
+
* heights kept: 48 Android / 42 iOS.
|
|
16
|
+
*/
|
|
17
|
+
export const ScreenHeader = ({ title, style }: ScreenHeaderProps) => (
|
|
18
|
+
<BaseHeader style={[styles.container, style]}>
|
|
19
|
+
<Typography style={styles.title} variant="header">
|
|
20
|
+
{title}
|
|
21
|
+
</Typography>
|
|
22
|
+
</BaseHeader>
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const styles = StyleSheet.create({
|
|
26
|
+
container: {
|
|
27
|
+
height: Platform.OS === "android" ? moderateScale(48) : moderateScale(42),
|
|
28
|
+
},
|
|
29
|
+
title: {
|
|
30
|
+
includeFontPadding: false,
|
|
31
|
+
textAlignVertical: "center",
|
|
32
|
+
...(Platform.OS === "android" ? { lineHeight: moderateScale(28) } : {}),
|
|
33
|
+
},
|
|
34
|
+
});
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { forwardRef, useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
TextInput,
|
|
4
|
+
View,
|
|
5
|
+
type StyleProp,
|
|
6
|
+
type TextInputProps,
|
|
7
|
+
type ViewStyle,
|
|
8
|
+
} from "react-native";
|
|
9
|
+
import { StyleSheet } from "react-native-unistyles";
|
|
10
|
+
|
|
11
|
+
import { Typography } from "../Typography";
|
|
12
|
+
|
|
13
|
+
export type InputProps = Omit<TextInputProps, "editable"> & {
|
|
14
|
+
label?: string;
|
|
15
|
+
error?: string;
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
containerStyle?: StyleProp<ViewStyle>;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/** Forwards its ref to the underlying TextInput, so forms can focus it. */
|
|
21
|
+
export const Input = forwardRef<TextInput, InputProps>(
|
|
22
|
+
(
|
|
23
|
+
{
|
|
24
|
+
label,
|
|
25
|
+
error,
|
|
26
|
+
disabled = false,
|
|
27
|
+
containerStyle,
|
|
28
|
+
style,
|
|
29
|
+
onFocus,
|
|
30
|
+
onBlur,
|
|
31
|
+
...rest
|
|
32
|
+
},
|
|
33
|
+
ref
|
|
34
|
+
) => {
|
|
35
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
36
|
+
|
|
37
|
+
styles.useVariants({ focused: isFocused, error: !!error, disabled });
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<View style={[styles.container, containerStyle]}>
|
|
41
|
+
{!!label && (
|
|
42
|
+
<Typography size="xs" style={styles.label}>
|
|
43
|
+
{label}
|
|
44
|
+
</Typography>
|
|
45
|
+
)}
|
|
46
|
+
<TextInput
|
|
47
|
+
// Before the spread: the label is only a fallback, and setting it
|
|
48
|
+
// afterwards overwrote a caller's own accessibilityLabel with
|
|
49
|
+
// undefined whenever no label was passed.
|
|
50
|
+
accessibilityLabel={label}
|
|
51
|
+
{...rest}
|
|
52
|
+
editable={!disabled}
|
|
53
|
+
ref={ref}
|
|
54
|
+
style={[styles.input, style]}
|
|
55
|
+
onBlur={event => {
|
|
56
|
+
setIsFocused(false);
|
|
57
|
+
onBlur?.(event);
|
|
58
|
+
}}
|
|
59
|
+
onFocus={event => {
|
|
60
|
+
setIsFocused(true);
|
|
61
|
+
onFocus?.(event);
|
|
62
|
+
}}
|
|
63
|
+
/>
|
|
64
|
+
{!!error && (
|
|
65
|
+
<Typography size="2xs" style={styles.error}>
|
|
66
|
+
{error}
|
|
67
|
+
</Typography>
|
|
68
|
+
)}
|
|
69
|
+
</View>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
Input.displayName = "Input";
|
|
75
|
+
|
|
76
|
+
const styles = StyleSheet.create(theme => ({
|
|
77
|
+
container: {
|
|
78
|
+
gap: theme.spacing.xs,
|
|
79
|
+
},
|
|
80
|
+
label: {
|
|
81
|
+
color: theme.colors.text.secondary,
|
|
82
|
+
fontFamily: theme.fonts.medium,
|
|
83
|
+
},
|
|
84
|
+
input: {
|
|
85
|
+
borderColor: theme.colors.border.primary,
|
|
86
|
+
borderRadius: theme.radii.md,
|
|
87
|
+
borderWidth: 1,
|
|
88
|
+
color: theme.colors.text.primary,
|
|
89
|
+
fontFamily: theme.fonts.regular,
|
|
90
|
+
fontSize: theme.fontSizes.s,
|
|
91
|
+
paddingHorizontal: theme.spacing.md,
|
|
92
|
+
paddingVertical: theme.spacing.sm,
|
|
93
|
+
variants: {
|
|
94
|
+
focused: {
|
|
95
|
+
true: { borderColor: theme.colors.primary },
|
|
96
|
+
false: {},
|
|
97
|
+
},
|
|
98
|
+
error: {
|
|
99
|
+
true: { borderColor: theme.colors.danger },
|
|
100
|
+
false: {},
|
|
101
|
+
},
|
|
102
|
+
disabled: {
|
|
103
|
+
true: {
|
|
104
|
+
backgroundColor: theme.colors.background.secondary,
|
|
105
|
+
opacity: 0.5,
|
|
106
|
+
},
|
|
107
|
+
false: {},
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
// Error must win over focus when both are active.
|
|
111
|
+
compoundVariants: [
|
|
112
|
+
{
|
|
113
|
+
error: true,
|
|
114
|
+
focused: true,
|
|
115
|
+
styles: { borderColor: theme.colors.danger },
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
},
|
|
119
|
+
error: {
|
|
120
|
+
color: theme.colors.danger,
|
|
121
|
+
},
|
|
122
|
+
}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Input, type InputProps } from "./Input";
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
import { View } from "react-native";
|
|
3
|
+
import Animated, {
|
|
4
|
+
Easing,
|
|
5
|
+
interpolate,
|
|
6
|
+
useAnimatedProps,
|
|
7
|
+
useAnimatedStyle,
|
|
8
|
+
useSharedValue,
|
|
9
|
+
withRepeat,
|
|
10
|
+
withTiming,
|
|
11
|
+
} from "react-native-reanimated";
|
|
12
|
+
import Svg, { Circle } from "react-native-svg";
|
|
13
|
+
import { StyleSheet } from "react-native-unistyles";
|
|
14
|
+
|
|
15
|
+
import { moderateScale } from "../../utils/scale";
|
|
16
|
+
import { palette } from "../../theme/palette";
|
|
17
|
+
|
|
18
|
+
const LOADER_WIDTH = {
|
|
19
|
+
s: moderateScale(18),
|
|
20
|
+
m: moderateScale(24),
|
|
21
|
+
l: moderateScale(36),
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const AnimatedCircle = Animated.createAnimatedComponent(Circle);
|
|
25
|
+
|
|
26
|
+
export type LoaderProps = {
|
|
27
|
+
// v2 default: the lightBlue400 accent, kept for parity.
|
|
28
|
+
color?: string;
|
|
29
|
+
size?: keyof typeof LOADER_WIDTH;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const Loader = ({
|
|
33
|
+
color = palette.lightBlue400,
|
|
34
|
+
size = "s",
|
|
35
|
+
}: LoaderProps) => {
|
|
36
|
+
const progress = useSharedValue(0);
|
|
37
|
+
const width = LOADER_WIDTH[size];
|
|
38
|
+
const strokeWidth = width * 0.15;
|
|
39
|
+
const radius = (width - strokeWidth) / 2;
|
|
40
|
+
const circumference = 2 * Math.PI * radius;
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
progress.value = withRepeat(
|
|
44
|
+
withTiming(2, { duration: 1500, easing: Easing.linear }),
|
|
45
|
+
-1,
|
|
46
|
+
false
|
|
47
|
+
);
|
|
48
|
+
}, [progress]);
|
|
49
|
+
|
|
50
|
+
const animatedProps = useAnimatedProps(() => ({
|
|
51
|
+
strokeDashoffset: -interpolate(
|
|
52
|
+
progress.value,
|
|
53
|
+
[0, 1, 2],
|
|
54
|
+
[0, circumference, circumference * 2]
|
|
55
|
+
),
|
|
56
|
+
}));
|
|
57
|
+
|
|
58
|
+
const animatedStyles = useAnimatedStyle(() => ({
|
|
59
|
+
width,
|
|
60
|
+
height: width,
|
|
61
|
+
alignItems: "center",
|
|
62
|
+
justifyContent: "center",
|
|
63
|
+
transform: [
|
|
64
|
+
{ rotate: `${interpolate(progress.value, [0, 2], [0, 420])}deg` },
|
|
65
|
+
],
|
|
66
|
+
}));
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<View style={styles.container} testID="neeto-loader">
|
|
70
|
+
<Animated.View style={animatedStyles}>
|
|
71
|
+
<Svg fill="none" height={width} width={width}>
|
|
72
|
+
<AnimatedCircle
|
|
73
|
+
animatedProps={animatedProps}
|
|
74
|
+
cx={width / 2}
|
|
75
|
+
cy={width / 2}
|
|
76
|
+
r={radius}
|
|
77
|
+
stroke={color}
|
|
78
|
+
strokeDasharray={circumference}
|
|
79
|
+
strokeLinecap="round"
|
|
80
|
+
strokeWidth={strokeWidth}
|
|
81
|
+
/>
|
|
82
|
+
</Svg>
|
|
83
|
+
</Animated.View>
|
|
84
|
+
</View>
|
|
85
|
+
);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const styles = StyleSheet.create({
|
|
89
|
+
container: {
|
|
90
|
+
alignItems: "center",
|
|
91
|
+
justifyContent: "center",
|
|
92
|
+
},
|
|
93
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Loader, type LoaderProps } from "./Loader";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { StyleSheet } from "react-native";
|
|
3
|
+
import { BottomSheetModalProvider } from "@gorhom/bottom-sheet";
|
|
4
|
+
import { GestureHandlerRootView } from "react-native-gesture-handler";
|
|
5
|
+
import { SafeAreaProvider } from "react-native-safe-area-context";
|
|
6
|
+
|
|
7
|
+
import { configureNeetoUI } from "../../unistyles";
|
|
8
|
+
import { Alert } from "../Alert";
|
|
9
|
+
import { Toast } from "../Toast";
|
|
10
|
+
|
|
11
|
+
export type NeetoUIProviderProps = {
|
|
12
|
+
children: ReactNode;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Single mount point for everything the library needs at app root.
|
|
17
|
+
*
|
|
18
|
+
* Unistyles registration runs before the first child render (idempotent) so
|
|
19
|
+
* lazily-resolved stylesheets see the themes. SafeAreaProvider feeds
|
|
20
|
+
* ParentView/BottomSheet insets, GestureHandlerRootView +
|
|
21
|
+
* BottomSheetModalProvider host BottomSheet portals, and the Toast host
|
|
22
|
+
* makes Toast.show work with no manual mounting — v2 required apps to
|
|
23
|
+
* render <Toast /> themselves; that footgun dies here.
|
|
24
|
+
*/
|
|
25
|
+
export const NeetoUIProvider = ({ children }: NeetoUIProviderProps) => {
|
|
26
|
+
// Idempotent fallback — the root entry already configures on import
|
|
27
|
+
// (src/unistyles-init.ts); this covers deep-import edge cases.
|
|
28
|
+
configureNeetoUI();
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<GestureHandlerRootView style={styles.root}>
|
|
32
|
+
<SafeAreaProvider>
|
|
33
|
+
<BottomSheetModalProvider>
|
|
34
|
+
{children}
|
|
35
|
+
<Alert />
|
|
36
|
+
<Toast />
|
|
37
|
+
</BottomSheetModalProvider>
|
|
38
|
+
</SafeAreaProvider>
|
|
39
|
+
</GestureHandlerRootView>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const styles = StyleSheet.create({ root: { flex: 1 } });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { NeetoUIProvider, type NeetoUIProviderProps } from "./NeetoUIProvider";
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { useRef, useState, type ComponentType, type ReactNode } from "react";
|
|
2
|
+
import {
|
|
3
|
+
FlatList,
|
|
4
|
+
View,
|
|
5
|
+
useWindowDimensions,
|
|
6
|
+
type NativeScrollEvent,
|
|
7
|
+
type NativeSyntheticEvent,
|
|
8
|
+
} from "react-native";
|
|
9
|
+
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
10
|
+
import { StyleSheet } from "react-native-unistyles";
|
|
11
|
+
|
|
12
|
+
import { moderateScale } from "../../utils/scale";
|
|
13
|
+
import { Button } from "../Button";
|
|
14
|
+
import { Typography } from "../Typography";
|
|
15
|
+
|
|
16
|
+
export type OnBoardingSlide = {
|
|
17
|
+
title: string;
|
|
18
|
+
description: string;
|
|
19
|
+
illustration?: ReactNode;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type OnBoardingProps = {
|
|
23
|
+
/** Rendered top-center; receives `width` (v2 contract). */
|
|
24
|
+
appLogo: ComponentType<{ width: number }>;
|
|
25
|
+
slides: OnBoardingSlide[];
|
|
26
|
+
onComplete: () => void;
|
|
27
|
+
logoWidth?: number;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* v2's OnBoarding sat on react-native-snap-carousel, which is unmaintained
|
|
32
|
+
* and dies in v3 — the pager is now an internal paging FlatList with the
|
|
33
|
+
* same dots. Public API is unchanged.
|
|
34
|
+
*/
|
|
35
|
+
export const OnBoarding = ({
|
|
36
|
+
appLogo: AppLogo,
|
|
37
|
+
slides,
|
|
38
|
+
onComplete,
|
|
39
|
+
logoWidth = moderateScale(150),
|
|
40
|
+
}: OnBoardingProps) => {
|
|
41
|
+
const [activeIndex, setActiveIndex] = useState(0);
|
|
42
|
+
const listRef = useRef<FlatList<OnBoardingSlide>>(null);
|
|
43
|
+
const { width } = useWindowDimensions();
|
|
44
|
+
const { bottom } = useSafeAreaInsets();
|
|
45
|
+
|
|
46
|
+
const isLastSlide = activeIndex === slides.length - 1;
|
|
47
|
+
|
|
48
|
+
const handleNext = () => {
|
|
49
|
+
if (isLastSlide) {
|
|
50
|
+
onComplete();
|
|
51
|
+
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const next = activeIndex + 1;
|
|
55
|
+
setActiveIndex(next);
|
|
56
|
+
listRef.current?.scrollToIndex({ index: next, animated: true });
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const handleMomentumEnd = (
|
|
60
|
+
event: NativeSyntheticEvent<NativeScrollEvent>
|
|
61
|
+
) => {
|
|
62
|
+
const index = Math.round(event.nativeEvent.contentOffset.x / width);
|
|
63
|
+
setActiveIndex(Math.max(0, Math.min(index, slides.length - 1)));
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<View style={styles.root}>
|
|
68
|
+
<View style={styles.logoContainer}>
|
|
69
|
+
<AppLogo width={logoWidth} />
|
|
70
|
+
</View>
|
|
71
|
+
<FlatList
|
|
72
|
+
horizontal
|
|
73
|
+
pagingEnabled
|
|
74
|
+
data={slides}
|
|
75
|
+
keyExtractor={(_item, index) => String(index)}
|
|
76
|
+
ref={listRef}
|
|
77
|
+
showsHorizontalScrollIndicator={false}
|
|
78
|
+
testID="onboarding-pager"
|
|
79
|
+
getItemLayout={(_data, index) => ({
|
|
80
|
+
length: width,
|
|
81
|
+
offset: width * index,
|
|
82
|
+
index,
|
|
83
|
+
})}
|
|
84
|
+
renderItem={({ item }) => (
|
|
85
|
+
<View style={[styles.slide, { width }]}>
|
|
86
|
+
<View style={styles.illustration}>{item.illustration}</View>
|
|
87
|
+
<View style={styles.slideText}>
|
|
88
|
+
<Typography size="4xl" style={styles.centered} weight="bold">
|
|
89
|
+
{item.title}
|
|
90
|
+
</Typography>
|
|
91
|
+
<Typography
|
|
92
|
+
size="l"
|
|
93
|
+
style={[styles.centered, styles.description]}
|
|
94
|
+
>
|
|
95
|
+
{item.description}
|
|
96
|
+
</Typography>
|
|
97
|
+
</View>
|
|
98
|
+
</View>
|
|
99
|
+
)}
|
|
100
|
+
onMomentumScrollEnd={handleMomentumEnd}
|
|
101
|
+
/>
|
|
102
|
+
<View accessibilityLabel="Page indicator" style={styles.dots}>
|
|
103
|
+
{slides.map((_slide, index) => (
|
|
104
|
+
<View
|
|
105
|
+
key={index}
|
|
106
|
+
style={[styles.dot, index !== activeIndex && styles.inactiveDot]}
|
|
107
|
+
/>
|
|
108
|
+
))}
|
|
109
|
+
</View>
|
|
110
|
+
<View
|
|
111
|
+
style={[
|
|
112
|
+
styles.buttonContainer,
|
|
113
|
+
bottom === 0 && styles.buttonContainerInsetless,
|
|
114
|
+
]}
|
|
115
|
+
>
|
|
116
|
+
<Button
|
|
117
|
+
label={isLastSlide ? "Get Started" : "Next"}
|
|
118
|
+
onPress={handleNext}
|
|
119
|
+
/>
|
|
120
|
+
</View>
|
|
121
|
+
</View>
|
|
122
|
+
);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const styles = StyleSheet.create(theme => ({
|
|
126
|
+
root: {
|
|
127
|
+
backgroundColor: theme.colors.background.primary,
|
|
128
|
+
flex: 1,
|
|
129
|
+
},
|
|
130
|
+
logoContainer: {
|
|
131
|
+
alignItems: "center",
|
|
132
|
+
},
|
|
133
|
+
slide: {
|
|
134
|
+
flex: 1,
|
|
135
|
+
justifyContent: "space-between",
|
|
136
|
+
},
|
|
137
|
+
illustration: {
|
|
138
|
+
alignItems: "center",
|
|
139
|
+
flex: 1,
|
|
140
|
+
justifyContent: "center",
|
|
141
|
+
marginHorizontal: moderateScale(24),
|
|
142
|
+
marginVertical: moderateScale(12),
|
|
143
|
+
},
|
|
144
|
+
slideText: {
|
|
145
|
+
alignItems: "center",
|
|
146
|
+
marginHorizontal: moderateScale(24),
|
|
147
|
+
marginVertical: moderateScale(12),
|
|
148
|
+
},
|
|
149
|
+
centered: {
|
|
150
|
+
textAlign: "center",
|
|
151
|
+
},
|
|
152
|
+
description: {
|
|
153
|
+
color: theme.colors.text.muted,
|
|
154
|
+
},
|
|
155
|
+
dots: {
|
|
156
|
+
alignItems: "center",
|
|
157
|
+
flexDirection: "row",
|
|
158
|
+
justifyContent: "center",
|
|
159
|
+
marginVertical: moderateScale(20),
|
|
160
|
+
},
|
|
161
|
+
dot: {
|
|
162
|
+
backgroundColor: theme.colors.text.primary,
|
|
163
|
+
borderRadius: moderateScale(5),
|
|
164
|
+
height: moderateScale(10),
|
|
165
|
+
marginHorizontal: moderateScale(4),
|
|
166
|
+
width: moderateScale(10),
|
|
167
|
+
},
|
|
168
|
+
inactiveDot: {
|
|
169
|
+
backgroundColor: theme.colors.palette.grey400,
|
|
170
|
+
},
|
|
171
|
+
buttonContainer: {
|
|
172
|
+
marginHorizontal: moderateScale(24),
|
|
173
|
+
},
|
|
174
|
+
buttonContainerInsetless: {
|
|
175
|
+
marginBottom: moderateScale(16),
|
|
176
|
+
},
|
|
177
|
+
}));
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Keyboard,
|
|
3
|
+
StatusBar,
|
|
4
|
+
TextInput,
|
|
5
|
+
type StatusBarStyle,
|
|
6
|
+
type ViewProps,
|
|
7
|
+
} from "react-native";
|
|
8
|
+
import {
|
|
9
|
+
SafeAreaView,
|
|
10
|
+
useSafeAreaInsets,
|
|
11
|
+
} from "react-native-safe-area-context";
|
|
12
|
+
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
|
13
|
+
|
|
14
|
+
import { Container } from "../Container";
|
|
15
|
+
|
|
16
|
+
export type ParentViewProps = ViewProps & {
|
|
17
|
+
barStyle?: StatusBarStyle;
|
|
18
|
+
/** Screen background. Defaults to the active theme's primary background. */
|
|
19
|
+
backgroundColor?: string;
|
|
20
|
+
topInset?: boolean;
|
|
21
|
+
rightInset?: boolean;
|
|
22
|
+
leftInset?: boolean;
|
|
23
|
+
bottomInset?: boolean;
|
|
24
|
+
shouldDismissKeyboardOnTap?: boolean;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const ParentView = ({
|
|
28
|
+
barStyle = "light-content",
|
|
29
|
+
children,
|
|
30
|
+
backgroundColor,
|
|
31
|
+
topInset = true,
|
|
32
|
+
rightInset = true,
|
|
33
|
+
leftInset = true,
|
|
34
|
+
bottomInset = true,
|
|
35
|
+
shouldDismissKeyboardOnTap = true,
|
|
36
|
+
style,
|
|
37
|
+
...rest
|
|
38
|
+
}: ParentViewProps) => {
|
|
39
|
+
const { theme } = useUnistyles();
|
|
40
|
+
const insets = useSafeAreaInsets();
|
|
41
|
+
|
|
42
|
+
const edges = [
|
|
43
|
+
...(topInset ? (["top"] as const) : []),
|
|
44
|
+
...(rightInset ? (["right"] as const) : []),
|
|
45
|
+
...(leftInset ? (["left"] as const) : []),
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
// v2 mapping: light-content status bar sits on the brand color, everything
|
|
49
|
+
// else on the plain background. background.primary rather than the literal
|
|
50
|
+
// `white` token, which stays #ffffff in both themes and would paint a white
|
|
51
|
+
// strip above a dark screen.
|
|
52
|
+
const statusBarColor =
|
|
53
|
+
barStyle === "light-content"
|
|
54
|
+
? theme.colors.primary
|
|
55
|
+
: theme.colors.background.primary;
|
|
56
|
+
|
|
57
|
+
const contentBackground = backgroundColor ?? theme.colors.background.primary;
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<SafeAreaView
|
|
61
|
+
edges={edges}
|
|
62
|
+
style={[styles.fill, { backgroundColor: statusBarColor }]}
|
|
63
|
+
>
|
|
64
|
+
<StatusBar backgroundColor={statusBarColor} barStyle={barStyle} />
|
|
65
|
+
<Container
|
|
66
|
+
{...(shouldDismissKeyboardOnTap
|
|
67
|
+
? {
|
|
68
|
+
onTouchStart: event => {
|
|
69
|
+
// TextInput.State is the only architecture-agnostic way to
|
|
70
|
+
// know a tap landed outside the focused input — Fabric event
|
|
71
|
+
// targets don't carry the Paper fiber internals. (Carried
|
|
72
|
+
// over from v2's New Architecture fix.)
|
|
73
|
+
const focusedInput = TextInput.State.currentlyFocusedInput();
|
|
74
|
+
if (focusedInput && event.target !== focusedInput) {
|
|
75
|
+
Keyboard.dismiss();
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
}
|
|
79
|
+
: {})}
|
|
80
|
+
{...rest}
|
|
81
|
+
style={[
|
|
82
|
+
styles.fill,
|
|
83
|
+
{ backgroundColor: contentBackground },
|
|
84
|
+
bottomInset && { paddingBottom: insets.bottom },
|
|
85
|
+
style,
|
|
86
|
+
]}
|
|
87
|
+
>
|
|
88
|
+
{children}
|
|
89
|
+
</Container>
|
|
90
|
+
</SafeAreaView>
|
|
91
|
+
);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const styles = StyleSheet.create({
|
|
95
|
+
fill: {
|
|
96
|
+
flex: 1,
|
|
97
|
+
},
|
|
98
|
+
});
|