399 lines
10 KiB
JavaScript
399 lines
10 KiB
JavaScript
import { useContext, useEffect, useState } from "react";
|
|
import { AppContext, AppStyles, IsPlatformIos } from "../../utils";
|
|
import {
|
|
KeyboardAvoidingView,
|
|
Modal,
|
|
ScrollView,
|
|
Text,
|
|
TextInput,
|
|
TouchableOpacity,
|
|
View,
|
|
} from "react-native";
|
|
import { Divider } from "../Divider";
|
|
import MyIcon from "../Icon";
|
|
import { useTranslation } from "react-i18next";
|
|
|
|
/*
|
|
transparent: set this to prevent modal reopening on iOS
|
|
https://github.com/facebook/react-native/issues/34018
|
|
*/
|
|
|
|
const modalContentStyle = { margin: 10, paddingTop: 10 };
|
|
|
|
export default function MyModal({
|
|
children,
|
|
isOpen,
|
|
closeModal,
|
|
header,
|
|
content,
|
|
disableAnimationForIos,
|
|
scrollView,
|
|
}) {
|
|
const appContext = useContext(AppContext);
|
|
|
|
const getContent = () => {
|
|
if (scrollView) {
|
|
return (
|
|
<ScrollView>
|
|
<View style={modalContentStyle}>{content}</View>
|
|
</ScrollView>
|
|
);
|
|
}
|
|
|
|
return <View style={modalContentStyle}>{content}</View>;
|
|
};
|
|
|
|
return (
|
|
<View>
|
|
<Modal
|
|
visible={isOpen}
|
|
animationType={
|
|
disableAnimationForIos && IsPlatformIos() ? "none" : "slide"
|
|
}
|
|
onRequestClose={() => closeModal()}
|
|
transparent
|
|
>
|
|
{children}
|
|
|
|
<View
|
|
style={[
|
|
{
|
|
flex: 1,
|
|
backgroundColor: appContext.appTheme.backgroundColor,
|
|
paddingTop: IsPlatformIos() ? 30 : 15,
|
|
},
|
|
,
|
|
]}
|
|
>
|
|
{header}
|
|
|
|
{getContent()}
|
|
</View>
|
|
</Modal>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
export function MyDefaultModalHeader({ title, closeModal }) {
|
|
const appContext = useContext(AppContext);
|
|
|
|
return (
|
|
<View
|
|
style={{
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
justifyContent: "space-between",
|
|
}}
|
|
>
|
|
<TouchableOpacity onPress={() => closeModal()}>
|
|
<MyIcon name="chevron-left" size={24} style={{ marginLeft: 20 }} />
|
|
</TouchableOpacity>
|
|
|
|
<Text
|
|
style={[
|
|
AppStyles.typography16,
|
|
{ fontWeight: "bold", color: appContext.appTheme.text },
|
|
]}
|
|
>
|
|
{title}
|
|
</Text>
|
|
|
|
<View style={{ marginRight: 40 }} />
|
|
</View>
|
|
);
|
|
}
|
|
|
|
export function MyPickerModalListItem({ onPress, itemName, itemSelected }) {
|
|
const appContext = useContext(AppContext);
|
|
|
|
return (
|
|
<>
|
|
<TouchableOpacity onPress={onPress}>
|
|
<View
|
|
style={{
|
|
flexDirection: "row",
|
|
justifyContent: "space-between",
|
|
marginLeft: 10,
|
|
marginRight: 10,
|
|
}}
|
|
>
|
|
<Text
|
|
style={[
|
|
AppStyles.typography16,
|
|
{ color: appContext.appTheme.text },
|
|
]}
|
|
>
|
|
{itemName}
|
|
</Text>
|
|
|
|
{itemSelected && (
|
|
<MyIcon
|
|
name="check"
|
|
size={24}
|
|
color={appContext.appTheme.colors.primary}
|
|
/>
|
|
)}
|
|
</View>
|
|
</TouchableOpacity>
|
|
|
|
<Divider />
|
|
</>
|
|
);
|
|
}
|
|
|
|
export function MyPickerModal({ isOpen, setIsOpen, items, searchFilter }) {
|
|
const appContext = useContext(AppContext);
|
|
const { t } = useTranslation();
|
|
const [providedItems, setProvidedItems] = useState([]);
|
|
const [searchFilterInput, setSearchFilterInput] = useState("");
|
|
|
|
useEffect(() => setProvidedItems(items), [items]);
|
|
|
|
const closeModal = () => {
|
|
setIsOpen(false);
|
|
setProvidedItems(items);
|
|
setSearchFilterInput("");
|
|
};
|
|
|
|
return (
|
|
<Modal
|
|
visible={isOpen}
|
|
animationType="slide"
|
|
onRequestClose={() => closeModal()}
|
|
>
|
|
<KeyboardAvoidingView
|
|
behavior={IsPlatformIos() ? "padding" : "height"}
|
|
style={[
|
|
{
|
|
flex: 1,
|
|
backgroundColor: appContext.appTheme.backgroundColor,
|
|
},
|
|
]}
|
|
>
|
|
<View style={{ flexDirection: "row", alignItems: "center" }}>
|
|
<TouchableOpacity onPress={() => closeModal()}>
|
|
<MyIcon
|
|
name={searchFilter ? "chevron-left" : "window-close"}
|
|
size={24}
|
|
style={{ marginLeft: 20, marginTop: 20, marginBottom: 20 }}
|
|
color={appContext.appTheme.icon}
|
|
/>
|
|
</TouchableOpacity>
|
|
|
|
{searchFilter && (
|
|
<View
|
|
style={{
|
|
flex: 1,
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
borderBottomWidth: 1,
|
|
borderColor: appContext.appTheme.textInputBottomColor,
|
|
marginLeft: 10,
|
|
marginRight: 10,
|
|
}}
|
|
>
|
|
<MyIcon
|
|
style={{ marginLeft: 10 }}
|
|
name="magnify"
|
|
size={22}
|
|
color={appContext.appTheme.icon}
|
|
/>
|
|
<TextInput
|
|
style={{
|
|
flex: 1,
|
|
height: 40,
|
|
marginLeft: 10,
|
|
marginRight: 10,
|
|
}}
|
|
placeholder={t("common.textInputPlaceholderSearch")}
|
|
value={searchFilterInput}
|
|
onChangeText={(text) => {
|
|
setSearchFilterInput(text);
|
|
|
|
setProvidedItems(() =>
|
|
items.filter((provItem) => {
|
|
return provItem.label
|
|
.toLowerCase()
|
|
.startsWith(text.toLowerCase());
|
|
})
|
|
);
|
|
}}
|
|
/>
|
|
{searchFilterInput !== "" && (
|
|
<TouchableOpacity
|
|
onPress={() => {
|
|
setSearchFilterInput("");
|
|
setProvidedItems(items);
|
|
}}
|
|
>
|
|
<MyIcon
|
|
style={{ marginRight: 10 }}
|
|
name="close"
|
|
size={20}
|
|
color={appContext.appTheme.icon}
|
|
/>
|
|
</TouchableOpacity>
|
|
)}
|
|
</View>
|
|
)}
|
|
</View>
|
|
|
|
{providedItems.length === 0 && (
|
|
<View
|
|
style={{
|
|
flex: 1,
|
|
justifyContent: "center",
|
|
alignItems: "center",
|
|
}}
|
|
>
|
|
<MyIcon
|
|
name="magnify-remove-outline"
|
|
color={appContext.appTheme.icon}
|
|
size={64}
|
|
/>
|
|
<Text style={[AppStyles.typography20, { marginTop: 20 }]}>
|
|
{t("common.pickerSearchFilterNoResults")}
|
|
</Text>
|
|
</View>
|
|
)}
|
|
|
|
{providedItems.map((item, i) => {
|
|
return item.selected ? (
|
|
<View key={i}>
|
|
<TouchableOpacity onPress={() => closeModal()}>
|
|
<View
|
|
style={{
|
|
flexDirection: "row",
|
|
justifyContent: "space-between",
|
|
marginLeft: 20,
|
|
marginRight: 20,
|
|
}}
|
|
>
|
|
<Text
|
|
style={[
|
|
AppStyles.typography16,
|
|
{ color: appContext.appTheme.text },
|
|
]}
|
|
>
|
|
{item.label}
|
|
</Text>
|
|
<MyIcon
|
|
name="check"
|
|
size={24}
|
|
color={appContext.appTheme.colors.primary}
|
|
/>
|
|
</View>
|
|
</TouchableOpacity>
|
|
<Divider />
|
|
</View>
|
|
) : (
|
|
<View key={i}>
|
|
<TouchableOpacity
|
|
onPress={() => {
|
|
closeModal();
|
|
item.onPress();
|
|
}}
|
|
>
|
|
<Text
|
|
style={[
|
|
AppStyles.typography16,
|
|
{ marginLeft: 20, color: appContext.appTheme.text },
|
|
]}
|
|
>
|
|
{item.label}
|
|
</Text>
|
|
</TouchableOpacity>
|
|
<Divider />
|
|
</View>
|
|
);
|
|
})}
|
|
</KeyboardAvoidingView>
|
|
</Modal>
|
|
);
|
|
}
|
|
|
|
export function MyTextInputModal({
|
|
isOpen,
|
|
setIsOpen,
|
|
onTextInputSave,
|
|
modalTitle,
|
|
inputTitle,
|
|
inputDescription,
|
|
}) {
|
|
const appContext = useContext(AppContext);
|
|
|
|
const closeModal = () => setIsOpen(false);
|
|
|
|
return (
|
|
<Modal
|
|
visible={isOpen}
|
|
animationType="slide"
|
|
onRequestClose={() => closeModal()}
|
|
>
|
|
<View
|
|
style={[
|
|
{
|
|
flex: 1,
|
|
backgroundColor: appContext.appTheme.backgroundColor,
|
|
},
|
|
]}
|
|
>
|
|
<View
|
|
style={{
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
justifyContent: "space-between",
|
|
marginTop: 20,
|
|
}}
|
|
>
|
|
<TouchableOpacity onPress={() => closeModal()}>
|
|
<MyIcon
|
|
name="chevron-left"
|
|
size={24}
|
|
style={{ marginLeft: 20 }}
|
|
color={appContext.appTheme.icon}
|
|
/>
|
|
</TouchableOpacity>
|
|
<Text style={AppStyles.typography20}>{modalTitle}</Text>
|
|
<TouchableOpacity
|
|
onPress={() => {
|
|
closeModal();
|
|
onTextInputSave();
|
|
}}
|
|
>
|
|
<MyIcon
|
|
name="check"
|
|
size={24}
|
|
style={{ marginRight: 20 }}
|
|
color={appContext.appTheme.colors.primary}
|
|
/>
|
|
</TouchableOpacity>
|
|
</View>
|
|
|
|
<View
|
|
style={{
|
|
flex: 1,
|
|
padding: 20,
|
|
}}
|
|
>
|
|
<Text style={{ color: "gray" }}>{inputTitle}</Text>
|
|
<TextInput
|
|
style={{
|
|
height: 40,
|
|
borderBottomWidth: 1,
|
|
borderColor: appContext.appTheme.textInputBottomColor,
|
|
}}
|
|
value="Turtle"
|
|
/>
|
|
<Text
|
|
style={[AppStyles.typography14, { color: "gray", marginTop: 6 }]}
|
|
>
|
|
{inputDescription}
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
</Modal>
|
|
);
|
|
}
|