@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,126 @@
|
|
|
1
|
+
import { useMemo, type Ref } from "react";
|
|
2
|
+
import { ScrollView, TextInput, View, type TextInputProps } from "react-native";
|
|
3
|
+
import { useMentions } from "react-native-controlled-mentions";
|
|
4
|
+
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
|
5
|
+
|
|
6
|
+
import { moderateScale } from "../../utils/scale";
|
|
7
|
+
import { Avatar } from "../Avatar";
|
|
8
|
+
import { Touchable } from "../Touchable";
|
|
9
|
+
import { Typography } from "../Typography";
|
|
10
|
+
import { buildMentionTriggerConfig, type MentionSuggestion } from "./mentions";
|
|
11
|
+
|
|
12
|
+
const MAX_VISIBLE_SUGGESTIONS = 4;
|
|
13
|
+
|
|
14
|
+
type MentionsInputProps = Omit<
|
|
15
|
+
TextInputProps,
|
|
16
|
+
"value" | "onChange" | "onChangeText" | "onSelectionChange"
|
|
17
|
+
> & {
|
|
18
|
+
value: string;
|
|
19
|
+
onChange: (value: string) => void;
|
|
20
|
+
suggestions?: MentionSuggestion[];
|
|
21
|
+
shouldShowSuggestions?: boolean;
|
|
22
|
+
inputRef?: Ref<TextInput>;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const MentionsInput = ({
|
|
26
|
+
value,
|
|
27
|
+
onChange,
|
|
28
|
+
suggestions = [],
|
|
29
|
+
shouldShowSuggestions = true,
|
|
30
|
+
inputRef,
|
|
31
|
+
style,
|
|
32
|
+
...rest
|
|
33
|
+
}: MentionsInputProps) => {
|
|
34
|
+
const { theme } = useUnistyles();
|
|
35
|
+
|
|
36
|
+
const triggersConfig = useMemo(
|
|
37
|
+
() =>
|
|
38
|
+
buildMentionTriggerConfig({
|
|
39
|
+
fontFamily: theme.fonts.bold,
|
|
40
|
+
color: theme.colors.primary,
|
|
41
|
+
}),
|
|
42
|
+
[theme]
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const { textInputProps, triggers } = useMentions({
|
|
46
|
+
value,
|
|
47
|
+
onChange,
|
|
48
|
+
triggersConfig,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const { keyword, onSelect } = triggers.mention;
|
|
52
|
+
|
|
53
|
+
const filteredSuggestions =
|
|
54
|
+
shouldShowSuggestions && keyword !== undefined
|
|
55
|
+
? suggestions
|
|
56
|
+
.filter(suggestion =>
|
|
57
|
+
suggestion.name
|
|
58
|
+
.toLocaleLowerCase()
|
|
59
|
+
.includes(keyword.toLocaleLowerCase())
|
|
60
|
+
)
|
|
61
|
+
.slice(0, MAX_VISIBLE_SUGGESTIONS)
|
|
62
|
+
: [];
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<View style={styles.container}>
|
|
66
|
+
{filteredSuggestions.length > 0 && (
|
|
67
|
+
<View style={styles.suggestionsCard} testID="mention-suggestions">
|
|
68
|
+
<ScrollView keyboardShouldPersistTaps="always">
|
|
69
|
+
{filteredSuggestions.map(suggestion => (
|
|
70
|
+
<Touchable
|
|
71
|
+
key={suggestion.id}
|
|
72
|
+
style={styles.suggestionRow}
|
|
73
|
+
onPress={() => onSelect(suggestion)}
|
|
74
|
+
>
|
|
75
|
+
<Avatar
|
|
76
|
+
imageUrl={suggestion.imageUrl}
|
|
77
|
+
name={suggestion.name}
|
|
78
|
+
variant="small"
|
|
79
|
+
/>
|
|
80
|
+
<Typography size="xs">{suggestion.name}</Typography>
|
|
81
|
+
</Touchable>
|
|
82
|
+
))}
|
|
83
|
+
</ScrollView>
|
|
84
|
+
</View>
|
|
85
|
+
)}
|
|
86
|
+
<TextInput
|
|
87
|
+
multiline
|
|
88
|
+
placeholderTextColor={theme.colors.text.muted}
|
|
89
|
+
ref={inputRef}
|
|
90
|
+
style={[styles.input, style]}
|
|
91
|
+
{...rest}
|
|
92
|
+
// Last on purpose: useMentions owns value, onChangeText and
|
|
93
|
+
// onSelectionChange, and losing any of them to a caller prop kills
|
|
94
|
+
// trigger detection silently.
|
|
95
|
+
{...textInputProps}
|
|
96
|
+
/>
|
|
97
|
+
</View>
|
|
98
|
+
);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const styles = StyleSheet.create(theme => ({
|
|
102
|
+
container: {
|
|
103
|
+
flex: 1,
|
|
104
|
+
marginVertical: moderateScale(10),
|
|
105
|
+
},
|
|
106
|
+
suggestionsCard: {
|
|
107
|
+
backgroundColor: theme.colors.background.secondary,
|
|
108
|
+
borderRadius: moderateScale(10),
|
|
109
|
+
maxHeight: moderateScale(200),
|
|
110
|
+
...theme.elevation.medium,
|
|
111
|
+
},
|
|
112
|
+
suggestionRow: {
|
|
113
|
+
alignItems: "center",
|
|
114
|
+
flexDirection: "row",
|
|
115
|
+
gap: moderateScale(8),
|
|
116
|
+
height: moderateScale(50),
|
|
117
|
+
paddingHorizontal: moderateScale(10),
|
|
118
|
+
},
|
|
119
|
+
input: {
|
|
120
|
+
color: theme.colors.text.primary,
|
|
121
|
+
fontFamily: theme.fonts.regular,
|
|
122
|
+
fontSize: theme.fontSizes.m,
|
|
123
|
+
minHeight: moderateScale(50),
|
|
124
|
+
padding: 0,
|
|
125
|
+
},
|
|
126
|
+
}));
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const OPTION_TYPES = {
|
|
2
|
+
REPLY: "REPLY",
|
|
3
|
+
NOTE: "NOTE",
|
|
4
|
+
FORWARD: "FORWARD",
|
|
5
|
+
} as const;
|
|
6
|
+
|
|
7
|
+
export type ChatInputOption = keyof typeof OPTION_TYPES;
|
|
8
|
+
|
|
9
|
+
export const PLACEHOLDERS: Record<ChatInputOption, string> = {
|
|
10
|
+
REPLY: "Type here to reply…",
|
|
11
|
+
NOTE: "Add note here…",
|
|
12
|
+
FORWARD: "Type here to forward…",
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const ACTION_LABELS: Partial<Record<ChatInputOption, string>> = {
|
|
16
|
+
NOTE: "Add note",
|
|
17
|
+
FORWARD: "Forward",
|
|
18
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export {
|
|
2
|
+
ChatInput,
|
|
3
|
+
type ChatInputPayload,
|
|
4
|
+
type ChatInputProps,
|
|
5
|
+
type ChatInputRef,
|
|
6
|
+
} from "./ChatInput";
|
|
7
|
+
export { OPTION_TYPES, type ChatInputOption } from "./constants";
|
|
8
|
+
export {
|
|
9
|
+
convertMentionsToHTMLAndPlainText,
|
|
10
|
+
type MentionSuggestion,
|
|
11
|
+
} from "./mentions";
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { type TriggersConfig } from "react-native-controlled-mentions";
|
|
2
|
+
|
|
3
|
+
export type MentionSuggestion = {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
imageUrl?: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
// v2 markup, produced by react-native-controlled-mentions 2.x and assumed
|
|
10
|
+
// by both apps' composer flows: @[Name](id). The 3.x lib defaults to
|
|
11
|
+
// {@}[Name](id), so the trigger is configured as a custom pattern that
|
|
12
|
+
// reads and writes the v2 shape — the value prop stays byte-compatible.
|
|
13
|
+
const MENTION_MARKUP = /@\[([^[]*?)]\(([^(]*?)\)/;
|
|
14
|
+
|
|
15
|
+
export const buildMentionTriggerConfig = (textStyle: {
|
|
16
|
+
fontFamily: string;
|
|
17
|
+
color: string;
|
|
18
|
+
}): TriggersConfig<"mention"> => ({
|
|
19
|
+
mention: {
|
|
20
|
+
trigger: "@",
|
|
21
|
+
allowedSpacesCount: 0,
|
|
22
|
+
isInsertSpaceAfterMention: true,
|
|
23
|
+
textStyle,
|
|
24
|
+
pattern: new RegExp(`(${MENTION_MARKUP.source})`, "g"),
|
|
25
|
+
getTriggerData: match => {
|
|
26
|
+
const [, name = "", id = ""] = MENTION_MARKUP.exec(match) ?? [];
|
|
27
|
+
|
|
28
|
+
return { original: match, trigger: "@", name, id };
|
|
29
|
+
},
|
|
30
|
+
getTriggerValue: suggestion => `@[${suggestion.name}](${suggestion.id})`,
|
|
31
|
+
getPlainString: data => `@${data.name}`,
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const HTML_ESCAPES: Record<string, string> = {
|
|
36
|
+
"&": "&",
|
|
37
|
+
"<": "<",
|
|
38
|
+
">": ">",
|
|
39
|
+
'"': """,
|
|
40
|
+
"'": "'",
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* v2 serialized through node-html-parser, which escaped on the way out.
|
|
45
|
+
* This port assembles HTML by hand, so it has to escape itself: the
|
|
46
|
+
* message body and the mention name/id all come from user-controlled text
|
|
47
|
+
* (a contact's display name is attacker-controlled in a support desk) and
|
|
48
|
+
* land in a document the web editors render.
|
|
49
|
+
*/
|
|
50
|
+
const escapeHtml = (value: string) =>
|
|
51
|
+
value.replace(/[&<>"']/g, character => HTML_ESCAPES[character] ?? character);
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Port of v2's converter, minus the node-html-parser dependency — the
|
|
55
|
+
* output span shape is fixed, so plain string assembly produces the same
|
|
56
|
+
* HTML the web editors consume.
|
|
57
|
+
*
|
|
58
|
+
* Divergence from v2: a mention whose id is missing from `suggestions`
|
|
59
|
+
* is left as-is instead of crashing on `suggestion.name`.
|
|
60
|
+
*/
|
|
61
|
+
export const convertMentionsToHTMLAndPlainText = ({
|
|
62
|
+
suggestions = [],
|
|
63
|
+
value,
|
|
64
|
+
}: {
|
|
65
|
+
suggestions?: MentionSuggestion[];
|
|
66
|
+
value: string;
|
|
67
|
+
}) => {
|
|
68
|
+
let html = `<p>${escapeHtml(value).replace(/\n/g, "<br>")}</p>`;
|
|
69
|
+
let plainText = value;
|
|
70
|
+
|
|
71
|
+
// The same pattern the trigger config writes with, so a display name
|
|
72
|
+
// containing a parenthesis is matched whole instead of being cut at the
|
|
73
|
+
// first ")".
|
|
74
|
+
for (const match of value.matchAll(new RegExp(MENTION_MARKUP.source, "g"))) {
|
|
75
|
+
const [mention, , id = ""] = match;
|
|
76
|
+
const suggestion = suggestions.find(item => item.id === id);
|
|
77
|
+
if (!suggestion) continue;
|
|
78
|
+
|
|
79
|
+
const name = escapeHtml(suggestion.name);
|
|
80
|
+
const span = `<span data-type="mention" data-id="${escapeHtml(
|
|
81
|
+
id
|
|
82
|
+
)}" data-label="${name}">@${name}</span>`;
|
|
83
|
+
// Replacers are functions so a name containing $& or $1 cannot be read
|
|
84
|
+
// as a replacement pattern; the html needle is escaped because the body
|
|
85
|
+
// it searches has already been escaped.
|
|
86
|
+
html = html.replace(escapeHtml(mention), () => span);
|
|
87
|
+
plainText = plainText.replace(mention, () => `@${suggestion.name}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return { html, plainText };
|
|
91
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Checkbox as CheckboxIcon,
|
|
3
|
+
CheckboxInactive,
|
|
4
|
+
} from "@bigbinary/neeto-icons-rn";
|
|
5
|
+
import { type StyleProp, type TextStyle } from "react-native";
|
|
6
|
+
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
|
7
|
+
|
|
8
|
+
import { moderateScale } from "../../utils/scale";
|
|
9
|
+
import { Touchable, type TouchableProps } from "../Touchable";
|
|
10
|
+
import { Typography } from "../Typography";
|
|
11
|
+
|
|
12
|
+
export type CheckBoxProps = Omit<TouchableProps, "onPress"> & {
|
|
13
|
+
checked?: boolean;
|
|
14
|
+
onSelect?: () => void;
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
label: string;
|
|
17
|
+
labelStyle?: StyleProp<TextStyle>;
|
|
18
|
+
isRequiredLabel?: boolean;
|
|
19
|
+
iconSize?: number;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const CheckBox = ({
|
|
23
|
+
checked = false,
|
|
24
|
+
onSelect,
|
|
25
|
+
disabled = false,
|
|
26
|
+
label,
|
|
27
|
+
labelStyle,
|
|
28
|
+
isRequiredLabel = false,
|
|
29
|
+
iconSize,
|
|
30
|
+
style,
|
|
31
|
+
...rest
|
|
32
|
+
}: CheckBoxProps) => {
|
|
33
|
+
const { theme } = useUnistyles();
|
|
34
|
+
|
|
35
|
+
styles.useVariants({ checked, disabled });
|
|
36
|
+
|
|
37
|
+
const Icon = checked ? CheckboxIcon : CheckboxInactive;
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<Touchable
|
|
41
|
+
accessibilityRole="checkbox"
|
|
42
|
+
accessibilityState={{ checked, disabled }}
|
|
43
|
+
disabled={disabled}
|
|
44
|
+
style={[styles.container, style]}
|
|
45
|
+
onPress={onSelect}
|
|
46
|
+
{...rest}
|
|
47
|
+
>
|
|
48
|
+
<Typography size="m" style={[styles.label, labelStyle]}>
|
|
49
|
+
{isRequiredLabel ? `${label}*` : label}
|
|
50
|
+
</Typography>
|
|
51
|
+
<Icon
|
|
52
|
+
color={theme.colors.text.primary}
|
|
53
|
+
size={iconSize ?? moderateScale(22)}
|
|
54
|
+
/>
|
|
55
|
+
</Touchable>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const styles = StyleSheet.create(theme => ({
|
|
60
|
+
container: {
|
|
61
|
+
alignItems: "center",
|
|
62
|
+
flexDirection: "row",
|
|
63
|
+
flexShrink: 1,
|
|
64
|
+
paddingVertical: moderateScale(8),
|
|
65
|
+
},
|
|
66
|
+
label: {
|
|
67
|
+
flex: 1,
|
|
68
|
+
lineHeight: moderateScale(22),
|
|
69
|
+
variants: {
|
|
70
|
+
checked: {
|
|
71
|
+
true: {
|
|
72
|
+
color: theme.colors.text.primary,
|
|
73
|
+
fontFamily: theme.fonts.semibold,
|
|
74
|
+
},
|
|
75
|
+
false: {
|
|
76
|
+
color: theme.colors.palette.grey400,
|
|
77
|
+
fontFamily: theme.fonts.regular,
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
disabled: {
|
|
81
|
+
true: { color: theme.colors.palette.grey400 },
|
|
82
|
+
false: {},
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { CheckBox, type CheckBoxProps } from "./CheckBox";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { View, type ViewProps } from "react-native";
|
|
2
|
+
|
|
3
|
+
export type ContainerProps = ViewProps;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The v2 workhorse (260 importing files fleet-wide), now a plain themed
|
|
7
|
+
* View. It accepted styled-system shorthands (`p`, `bg`, `flex`…) through
|
|
8
|
+
* the compat layer up to the v3 alpha; those are gone — pass `style`.
|
|
9
|
+
*/
|
|
10
|
+
export const Container = ({ children, style, ...rest }: ContainerProps) => (
|
|
11
|
+
<View {...rest} style={style}>
|
|
12
|
+
{children}
|
|
13
|
+
</View>
|
|
14
|
+
);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Container, type ContainerProps } from "./Container";
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { View, type ViewProps } from "react-native";
|
|
2
|
+
import { StyleSheet } from "react-native-unistyles";
|
|
3
|
+
|
|
4
|
+
import { moderateScale } from "../../utils/scale";
|
|
5
|
+
|
|
6
|
+
export type DividerProps = ViewProps & {
|
|
7
|
+
orientation?: "horizontal" | "vertical";
|
|
8
|
+
thickness?: number;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const Divider = ({
|
|
12
|
+
orientation = "horizontal",
|
|
13
|
+
thickness = moderateScale(1),
|
|
14
|
+
style,
|
|
15
|
+
...rest
|
|
16
|
+
}: DividerProps) => {
|
|
17
|
+
if (thickness <= 0) return null;
|
|
18
|
+
|
|
19
|
+
const lineStyle =
|
|
20
|
+
orientation === "horizontal"
|
|
21
|
+
? { height: thickness }
|
|
22
|
+
: { width: thickness, flex: 1 };
|
|
23
|
+
|
|
24
|
+
// styles.line first so a caller's style can recolor the rule.
|
|
25
|
+
return <View {...rest} style={[styles.line, lineStyle, style]} />;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const styles = StyleSheet.create(theme => ({
|
|
29
|
+
line: {
|
|
30
|
+
backgroundColor: theme.colors.palette.grey400,
|
|
31
|
+
},
|
|
32
|
+
}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Divider, type DividerProps } from "./Divider";
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { type ComponentType, type ReactNode } from "react";
|
|
2
|
+
import { View, type StyleProp, type ViewStyle } from "react-native";
|
|
3
|
+
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
|
4
|
+
|
|
5
|
+
import { moderateScale } from "../../utils/scale";
|
|
6
|
+
import { Button } from "../Button";
|
|
7
|
+
import { Typography } from "../Typography";
|
|
8
|
+
|
|
9
|
+
export type EmptyViewProps = {
|
|
10
|
+
title: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
/** Full-size artwork; product illustrations stay app-side. */
|
|
13
|
+
Illustration?: ReactNode;
|
|
14
|
+
/** Compact fallback when there is no illustration (neeto-icons-rn shape). */
|
|
15
|
+
Icon?: ComponentType<{ size?: number; color?: string }>;
|
|
16
|
+
primaryAction?: {
|
|
17
|
+
label: string;
|
|
18
|
+
onPress: () => void;
|
|
19
|
+
};
|
|
20
|
+
style?: StyleProp<ViewStyle>;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Promoted component: the fleet grew 10 hand-rolled empty states across 8
|
|
25
|
+
* codebases (fresh audit, 2026-07-28). One layout — artwork, title,
|
|
26
|
+
* description, optional CTA — replaces them. cal's API maps over:
|
|
27
|
+
* subtitle → description, showCTA/ctaLabel/onCTAPress → primaryAction.
|
|
28
|
+
*/
|
|
29
|
+
export const EmptyView = ({
|
|
30
|
+
title,
|
|
31
|
+
description,
|
|
32
|
+
Illustration,
|
|
33
|
+
Icon,
|
|
34
|
+
primaryAction,
|
|
35
|
+
style,
|
|
36
|
+
}: EmptyViewProps) => {
|
|
37
|
+
const { theme } = useUnistyles();
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<View style={[styles.container, style]}>
|
|
41
|
+
{Illustration ?? null}
|
|
42
|
+
{!Illustration && Icon && (
|
|
43
|
+
<Icon color={theme.colors.text.muted} size={moderateScale(48)} />
|
|
44
|
+
)}
|
|
45
|
+
<View style={styles.textBlock}>
|
|
46
|
+
<Typography size="xl" style={styles.centered} weight="semibold">
|
|
47
|
+
{title}
|
|
48
|
+
</Typography>
|
|
49
|
+
{!!description && (
|
|
50
|
+
<Typography size="s" style={[styles.centered, styles.description]}>
|
|
51
|
+
{description}
|
|
52
|
+
</Typography>
|
|
53
|
+
)}
|
|
54
|
+
</View>
|
|
55
|
+
{primaryAction && (
|
|
56
|
+
<Button label={primaryAction.label} onPress={primaryAction.onPress} />
|
|
57
|
+
)}
|
|
58
|
+
</View>
|
|
59
|
+
);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const styles = StyleSheet.create(theme => ({
|
|
63
|
+
container: {
|
|
64
|
+
alignItems: "center",
|
|
65
|
+
gap: moderateScale(16),
|
|
66
|
+
justifyContent: "center",
|
|
67
|
+
paddingHorizontal: moderateScale(24),
|
|
68
|
+
paddingVertical: moderateScale(32),
|
|
69
|
+
},
|
|
70
|
+
textBlock: {
|
|
71
|
+
alignItems: "center",
|
|
72
|
+
gap: moderateScale(4),
|
|
73
|
+
},
|
|
74
|
+
centered: {
|
|
75
|
+
textAlign: "center",
|
|
76
|
+
},
|
|
77
|
+
description: {
|
|
78
|
+
color: theme.colors.text.muted,
|
|
79
|
+
},
|
|
80
|
+
}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { EmptyView, type EmptyViewProps } from "./EmptyView";
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import {
|
|
2
|
+
forwardRef,
|
|
3
|
+
useCallback,
|
|
4
|
+
useEffect,
|
|
5
|
+
useMemo,
|
|
6
|
+
type ReactElement,
|
|
7
|
+
} from "react";
|
|
8
|
+
import { RefreshControl } from "react-native";
|
|
9
|
+
import {
|
|
10
|
+
FlashList as ShopifyFlashList,
|
|
11
|
+
type FlashListProps as ShopifyFlashListProps,
|
|
12
|
+
type FlashListRef,
|
|
13
|
+
} from "@shopify/flash-list";
|
|
14
|
+
import Animated, {
|
|
15
|
+
cancelAnimation,
|
|
16
|
+
Extrapolation,
|
|
17
|
+
interpolate,
|
|
18
|
+
useAnimatedStyle,
|
|
19
|
+
useSharedValue,
|
|
20
|
+
withSequence,
|
|
21
|
+
withTiming,
|
|
22
|
+
} from "react-native-reanimated";
|
|
23
|
+
|
|
24
|
+
import { useRefreshByUser } from "../../hooks";
|
|
25
|
+
import { moderateScale } from "../../utils/scale";
|
|
26
|
+
import { Skeleton } from "../Skeleton";
|
|
27
|
+
|
|
28
|
+
// Default loading row — v2 rendered a react-content-loader rect here; the
|
|
29
|
+
// promoted Skeleton primitive replaces it.
|
|
30
|
+
const Placeholder = () => (
|
|
31
|
+
<Skeleton
|
|
32
|
+
height={moderateScale(50)}
|
|
33
|
+
style={{
|
|
34
|
+
marginHorizontal: moderateScale(15),
|
|
35
|
+
marginBottom: moderateScale(10),
|
|
36
|
+
}}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
export type FlashListProps<T> = Omit<ShopifyFlashListProps<T>, "data"> & {
|
|
41
|
+
data?: readonly T[];
|
|
42
|
+
isLoading?: boolean;
|
|
43
|
+
SkeletonComponent?: ReactElement;
|
|
44
|
+
placeHolderItemCount?: number;
|
|
45
|
+
animationDuration?: number;
|
|
46
|
+
onRefresh?: () => Promise<unknown> | unknown;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* v2-parity FlashList: skeleton rows while loading, staggered fade-in of the
|
|
51
|
+
* first rows, pull-to-refresh wired through useRefreshByUser.
|
|
52
|
+
*/
|
|
53
|
+
const FlashListInner = <T,>(
|
|
54
|
+
{
|
|
55
|
+
animationDuration = 2000,
|
|
56
|
+
SkeletonComponent,
|
|
57
|
+
placeHolderItemCount = 10,
|
|
58
|
+
isLoading = false,
|
|
59
|
+
data = [],
|
|
60
|
+
onRefresh,
|
|
61
|
+
onEndReached,
|
|
62
|
+
keyExtractor,
|
|
63
|
+
renderItem: originalRenderItem,
|
|
64
|
+
...rest
|
|
65
|
+
}: FlashListProps<T>,
|
|
66
|
+
ref: React.Ref<FlashListRef<T>>
|
|
67
|
+
) => {
|
|
68
|
+
const progress = useSharedValue(0);
|
|
69
|
+
const { isRefetchingByUser, refetchByUser } = useRefreshByUser(
|
|
70
|
+
onRefresh ?? (() => {})
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
progress.value = 0;
|
|
75
|
+
progress.value = withSequence(
|
|
76
|
+
withTiming(20, { duration: animationDuration }),
|
|
77
|
+
withTiming(10000, { duration: 0 })
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
return () => cancelAnimation(progress);
|
|
81
|
+
}, [animationDuration, progress, isLoading]);
|
|
82
|
+
|
|
83
|
+
const FadeIn = useCallback(
|
|
84
|
+
({ index, children }: { index: number; children: React.ReactNode }) => {
|
|
85
|
+
const inputRange = [index - 1, index, index + 1, index + 2];
|
|
86
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
87
|
+
const animatedStyle = useAnimatedStyle(() => ({
|
|
88
|
+
opacity: interpolate(
|
|
89
|
+
progress.value,
|
|
90
|
+
inputRange,
|
|
91
|
+
[0, 0.6, 1, 1],
|
|
92
|
+
Extrapolation.CLAMP
|
|
93
|
+
),
|
|
94
|
+
transform: [
|
|
95
|
+
{
|
|
96
|
+
translateX: interpolate(
|
|
97
|
+
progress.value,
|
|
98
|
+
inputRange,
|
|
99
|
+
[25, 15, 10, 0],
|
|
100
|
+
Extrapolation.CLAMP
|
|
101
|
+
),
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
}));
|
|
105
|
+
|
|
106
|
+
return <Animated.View style={animatedStyle}>{children}</Animated.View>;
|
|
107
|
+
},
|
|
108
|
+
[progress]
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
// Memoized so the data prop keeps its identity across the parent's
|
|
112
|
+
// re-renders while loading, instead of remounting every placeholder row.
|
|
113
|
+
const dummyData = useMemo(
|
|
114
|
+
() => Array.from({ length: placeHolderItemCount }) as T[],
|
|
115
|
+
[placeHolderItemCount]
|
|
116
|
+
);
|
|
117
|
+
const shouldAnimate = animationDuration > 0;
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<ShopifyFlashList<T>
|
|
121
|
+
{...rest}
|
|
122
|
+
data={isLoading ? dummyData : (data as T[])}
|
|
123
|
+
extraData={{ isLoading }}
|
|
124
|
+
ref={ref}
|
|
125
|
+
keyExtractor={(item, index) =>
|
|
126
|
+
isLoading || !keyExtractor
|
|
127
|
+
? index.toString()
|
|
128
|
+
: keyExtractor(item, index)
|
|
129
|
+
}
|
|
130
|
+
refreshControl={
|
|
131
|
+
onRefresh ? (
|
|
132
|
+
<RefreshControl
|
|
133
|
+
refreshing={isRefetchingByUser}
|
|
134
|
+
onRefresh={refetchByUser}
|
|
135
|
+
/>
|
|
136
|
+
) : undefined
|
|
137
|
+
}
|
|
138
|
+
renderItem={info => {
|
|
139
|
+
if (isLoading) {
|
|
140
|
+
return (
|
|
141
|
+
<FadeIn index={info.index}>
|
|
142
|
+
{SkeletonComponent ?? <Placeholder />}
|
|
143
|
+
</FadeIn>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const rendered = originalRenderItem?.(info) ?? null;
|
|
148
|
+
|
|
149
|
+
return shouldAnimate ? (
|
|
150
|
+
<FadeIn index={info.index}>{rendered}</FadeIn>
|
|
151
|
+
) : (
|
|
152
|
+
rendered
|
|
153
|
+
);
|
|
154
|
+
}}
|
|
155
|
+
onEndReached={isLoading ? undefined : onEndReached}
|
|
156
|
+
/>
|
|
157
|
+
);
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
export const FlashList = forwardRef(FlashListInner) as <T>(
|
|
161
|
+
props: FlashListProps<T> & { ref?: React.Ref<FlashListRef<T>> }
|
|
162
|
+
) => ReactElement;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { FlashList, type FlashListProps } from "./FlashList";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { View, type ViewProps } from "react-native";
|
|
2
|
+
import { StyleSheet } from "react-native-unistyles";
|
|
3
|
+
|
|
4
|
+
export type BaseHeaderProps = ViewProps;
|
|
5
|
+
|
|
6
|
+
/** Bare header row surface (promoted from neeto-commons-rn). */
|
|
7
|
+
export const BaseHeader = ({ style, children, ...rest }: BaseHeaderProps) => (
|
|
8
|
+
<View {...rest} style={[styles.container, style]}>
|
|
9
|
+
{children}
|
|
10
|
+
</View>
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
const styles = StyleSheet.create(theme => ({
|
|
14
|
+
container: {
|
|
15
|
+
alignItems: "center",
|
|
16
|
+
backgroundColor: theme.colors.background.primary,
|
|
17
|
+
flexDirection: "row",
|
|
18
|
+
},
|
|
19
|
+
}));
|