Explorar el Código

created Date & Number input

Ian Fung hace 1 año
padre
commit
9575e97a1a

+ 32 - 6
app/(tabs)/settings/index.tsx

@@ -1,6 +1,7 @@
-import React, { useState } from "react";
-import { Text } from "react-native";
+import React, { useEffect, useState } from "react";
 import PhoneInput from "../../../component/global/phone_input";
+import NumberInput from "../../../component/global/number_input";
+import DateModal from "../../../component/global/date_input";
 export default function Index() {
 	/**********************************狀態管理**********************************/
 	/**********************************狀態管理**********************************/
@@ -9,20 +10,45 @@ export default function Index() {
 	/**********************************異步函數**********************************/
 	/**********************************異步函數**********************************/
 
-	const [inputValidationStatus, setInputValidationStatus] = useState(false);
+	//see if phone input is valid (enter at least 8 digits)
+	const [phoneInputValidationStatus, setPhoneInputValidationStatus] = useState(false);
 	const handleValidationStatus = (inputValidationStatus: boolean) => {
-		setInputValidationStatus(inputValidationStatus);
+		setPhoneInputValidationStatus(inputValidationStatus);
 	};
 
+	//testing to see if parent component receives the value from child component
+	const [phoneValue, setPhoneValue] = useState<string>("");
+	const updateParentPhoneValue = (value: string) => {
+		setPhoneValue(value);
+	};
+	const [numberValue, setNumberValue] = useState<string>("");
+	const updateParentNumberValue = (value: string) => {
+		setNumberValue(value);
+	};
+
+	const [dateValue, setDateValue] = useState<string>("");
+	const updateParentDateValue = (date: string) => {
+		setDateValue(date);
+	};
+
+	//console loggin to see if parent component receives the value from child component
+	useEffect(() => {
+		console.log(`Received inputValidationStatus value: ${phoneInputValidationStatus}`);
+		console.log(`Received phone value: ${phoneValue}`);
+		console.log(`Received number value :${numberValue}`);
+		console.log(`Received date value :${dateValue}`);
+	}, [phoneValue, numberValue, dateValue]);
+
 	return (
 		////testing component here
 		<>
 			<PhoneInput
 				placeholder="電話號碼"
-				onChangeText={(newText) => console.log(newText)}
+				onChangeText={updateParentPhoneValue}
 				onValidationStatusChange={handleValidationStatus}
 			/>
-			{console.log(inputValidationStatus)}
+			<NumberInput placeholder="OTP驗證碼" onChangeText={updateParentNumberValue} />
+			<DateModal onDateChange={updateParentDateValue} />
 		</>
 	);
 }

+ 94 - 0
component/global/date_input.tsx

@@ -0,0 +1,94 @@
+import { useState } from "react";
+import { StatusBar } from "expo-status-bar";
+import { StyleSheet, Text, TouchableOpacity, View, Modal, Pressable, TextInput } from "react-native";
+import DatePicker from "react-native-modern-datepicker";
+
+type DateModalProps = {
+	onDateChange: (date: string) => void;
+};
+const DateModal: React.FC<DateModalProps> = ({ onDateChange }) => {
+	const [open, setOpen] = useState(false);
+	const [date, setDate] = useState("");
+
+	//open or close date calendar modal
+	function openOrCloseModal() {
+		setOpen(!open);
+	}
+
+	//turn date from yyyy/mm/dd to dd/mm/yyyy && send info back to parent component
+	function handleChange(propDate: string) {
+		const parts = propDate.split("/");
+		const rearrangedParts = [parts[2], parts[1], parts[0].slice(-2)];
+		const formattedDate = rearrangedParts.join("/");
+		setDate(formattedDate);
+		onDateChange(formattedDate);
+	}
+
+	return (
+		<View style={styles.container}>
+			<Pressable onPress={openOrCloseModal} style={[styles.inputContainer]}>
+				<Text style={styles.placeholder}>{date ? date : "DD/MM/YY"}</Text>
+			</Pressable>
+			<Modal animationType="slide" transparent={true} visible={open}>
+				<View style={styles.centeredView}>
+					<View style={styles.modalView}>
+						<DatePicker mode="calendar" selected={date} onDateChange={handleChange} />
+						<TouchableOpacity onPress={openOrCloseModal}>
+							<Text style={styles.closeButton}>Close</Text>
+						</TouchableOpacity>
+					</View>
+				</View>
+			</Modal>
+			<StatusBar style="auto" />
+		</View>
+	);
+};
+
+const styles = StyleSheet.create({
+	container: {
+		flex: 1,
+		alignItems: "center",
+		justifyContent: "center",
+	},
+	inputContainer: {
+		width: "80%",
+		height: 60,
+		maxWidth: "100%",
+		flexDirection: "row",
+		alignItems: "center",
+		borderWidth: 1,
+		borderColor: "#bbbbbb",
+		borderRadius: 10,
+		padding: 15,
+		margin: 10,
+	},
+	placeholder: {
+		fontSize: 16,
+		color: "#888",
+	},
+	centeredView: {
+		flex: 1,
+		justifyContent: "center",
+		alignItems: "center",
+		marginTop: 22,
+	},
+	modalView: {
+		margin: 20,
+		backgroundColor: "white",
+		borderRadius: 20,
+		width: "90%",
+		padding: 20,
+		alignItems: "center",
+		shadowColor: "#000",
+		shadowOffset: {
+			width: 0,
+			height: 2,
+		},
+		shadowOpacity: 0.25,
+		shadowRadius: 4,
+		elevation: 5,
+	},
+	closeButton: { color: "#3f4f5c" },
+});
+
+export default DateModal;

+ 71 - 0
component/global/number_input.tsx

@@ -0,0 +1,71 @@
+import React, { useState } from "react";
+import { View, Text, TextInput, StyleSheet, ViewStyle, StyleProp } from "react-native";
+
+interface NumberInputProps {
+	placeholder: string;
+	extendedStyle?: StyleProp<ViewStyle>;
+	onChangeText: (text: string) => void;
+}
+
+const NumberInput: React.FC<NumberInputProps> = ({ placeholder, extendedStyle, onChangeText }) => {
+	const [inputValue, setInputValue] = useState<string>("");
+	const [error, setError] = useState("");
+
+	//if users input non-number, show error message
+	const validateInput = (input: string) => {
+		const regex = /^[0-9\b]+$/;
+		if (!input.trim()) {
+			setError("");
+			setInputValue(input);
+
+			onChangeText(input);
+			return;
+		}
+		if (regex.test(input)) {
+			setInputValue(input);
+			setError("");
+			onChangeText(input);
+		} else {
+			setError("Please enter only numbers");
+		}
+	};
+	return (
+		<View>
+			<View style={[styles.inputContainer, extendedStyle]}>
+				<TextInput
+					keyboardType="numeric"
+					placeholder={placeholder}
+					style={[styles.input]}
+					onChangeText={(text) => validateInput(text)}
+					value={inputValue}
+				/>
+			</View>
+			{error && <Text style={styles.errorMessage}>{error}</Text>}
+		</View>
+	);
+};
+
+const styles = StyleSheet.create({
+	inputContainer: {
+		maxWidth: "100%",
+		flexDirection: "row",
+		alignItems: "center",
+		justifyContent: "center",
+		borderWidth: 1,
+		borderColor: "#bbbbbb",
+		borderRadius: 10,
+		padding: 15,
+		margin: 10,
+		width: 120,
+	},
+	input: {
+		flex: 1,
+		marginLeft: 5,
+	},
+	errorMessage: {
+		fontSize: 12,
+		color: "#ff0033",
+		marginLeft: 20,
+	},
+});
+export default NumberInput;

+ 86 - 7
package-lock.json

@@ -9,6 +9,7 @@
       "version": "1.0.0",
       "dependencies": {
         "@reduxjs/toolkit": "^2.2.1",
+        "@types/react-native-datepicker": "^1.7.6",
         "axios": "^1.6.7",
         "dotenv": "^16.4.5",
         "expo": "~50.0.11",
@@ -21,6 +22,7 @@
         "react": "18.2.0",
         "react-native": "0.73.4",
         "react-native-maps": "1.10.0",
+        "react-native-modern-datepicker": "^1.0.0-beta.91",
         "react-native-safe-area-context": "4.8.2",
         "react-native-screens": "~3.29.0",
         "react-redux": "^9.1.0",
@@ -29,6 +31,7 @@
       "devDependencies": {
         "@babel/core": "^7.20.0",
         "@types/react": "~18.2.45",
+        "@types/react-native-modern-datepicker": "^1.0.5",
         "typescript": "^5.1.3"
       }
     },
@@ -6301,25 +6304,41 @@
     "node_modules/@types/prop-types": {
       "version": "15.7.11",
       "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
-      "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==",
-      "devOptional": true
+      "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng=="
     },
     "node_modules/@types/react": {
       "version": "18.2.65",
       "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.65.tgz",
       "integrity": "sha512-98TsY0aW4jqx/3RqsUXwMDZSWR1Z4CUlJNue8ueS2/wcxZOsz4xmW1X8ieaWVRHcmmQM3R8xVA4XWB3dJnWwDQ==",
-      "devOptional": true,
       "dependencies": {
         "@types/prop-types": "*",
         "@types/scheduler": "*",
         "csstype": "^3.0.2"
       }
     },
+    "node_modules/@types/react-native-datepicker": {
+      "version": "1.7.6",
+      "resolved": "https://registry.npmjs.org/@types/react-native-datepicker/-/react-native-datepicker-1.7.6.tgz",
+      "integrity": "sha512-CwcCBpaz3TINH2XbEdKCf+bd6dv/u2kRDapyC9BB/g/b0bfvidRxeWGV1sH8HeGr9e005o8U9UvyenOfIr9cqA==",
+      "dependencies": {
+        "@types/react": "*",
+        "moment": ">=2.14.0",
+        "react-native": "*"
+      }
+    },
+    "node_modules/@types/react-native-modern-datepicker": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/@types/react-native-modern-datepicker/-/react-native-modern-datepicker-1.0.5.tgz",
+      "integrity": "sha512-YO9t6nyKCLa90PPLq2OsBhGQU9PlRKSrzpYbz7hbeQ0RJDft1YiNTqktQbRN1g64qmlmjNPbMOFSuV7NpdgNzg==",
+      "dev": true,
+      "dependencies": {
+        "@types/react": "*"
+      }
+    },
     "node_modules/@types/scheduler": {
       "version": "0.16.8",
       "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz",
-      "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==",
-      "devOptional": true
+      "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A=="
     },
     "node_modules/@types/stack-utils": {
       "version": "2.0.3",
@@ -7648,8 +7667,7 @@
     "node_modules/csstype": {
       "version": "3.1.3",
       "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
-      "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
-      "devOptional": true
+      "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
     },
     "node_modules/cycle": {
       "version": "1.0.3",
@@ -9392,6 +9410,11 @@
       "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
       "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g=="
     },
+    "node_modules/jalaali-js": {
+      "version": "1.2.6",
+      "resolved": "https://registry.npmjs.org/jalaali-js/-/jalaali-js-1.2.6.tgz",
+      "integrity": "sha512-io974va+Qyu+UfuVX3UIAgJlxLhAMx9Y8VMfh+IG00Js7hXQo1qNQuwSiSa0xxco0SVgx5HWNkaiCcV+aZ8WPw=="
+    },
     "node_modules/jest-environment-node": {
       "version": "29.7.0",
       "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz",
@@ -11139,6 +11162,50 @@
         "mkdirp": "bin/cmd.js"
       }
     },
+    "node_modules/moment": {
+      "version": "2.30.1",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
+      "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/moment-jalaali": {
+      "version": "0.9.6",
+      "resolved": "https://registry.npmjs.org/moment-jalaali/-/moment-jalaali-0.9.6.tgz",
+      "integrity": "sha512-v8wXjQplvk5ez+sUqgsWIrafwIf1BEXXvzTYwsg1wHcqh27nSgKPCJ6FnZRrCz03MoNyB9N31L0oms+vE8Rq7g==",
+      "dependencies": {
+        "jalaali-js": "^1.1.0",
+        "moment": "^2.22.2",
+        "moment-timezone": "^0.5.21",
+        "rimraf": "^3.0.2"
+      }
+    },
+    "node_modules/moment-jalaali/node_modules/rimraf": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+      "dependencies": {
+        "glob": "^7.1.3"
+      },
+      "bin": {
+        "rimraf": "bin.js"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/moment-timezone": {
+      "version": "0.5.45",
+      "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.45.tgz",
+      "integrity": "sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ==",
+      "dependencies": {
+        "moment": "^2.29.4"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
     "node_modules/mrmime": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz",
@@ -12295,6 +12362,18 @@
         }
       }
     },
+    "node_modules/react-native-modern-datepicker": {
+      "version": "1.0.0-beta.91",
+      "resolved": "https://registry.npmjs.org/react-native-modern-datepicker/-/react-native-modern-datepicker-1.0.0-beta.91.tgz",
+      "integrity": "sha512-tyyS6iswwxn/CWIgk9j82X5/9gkgAQuI4fiWa+TEWjOo1WIhBr0x7914mtagrVuVgJCMRnHCopCIWL78afakHw==",
+      "dependencies": {
+        "moment-jalaali": "^0.9.2",
+        "prop-types": "^15.7.2"
+      },
+      "peerDependencies": {
+        "react-native": ">=0.59"
+      }
+    },
     "node_modules/react-native-safe-area-context": {
       "version": "4.8.2",
       "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.8.2.tgz",

+ 3 - 0
package.json

@@ -10,6 +10,7 @@
   },
   "dependencies": {
     "@reduxjs/toolkit": "^2.2.1",
+    "@types/react-native-datepicker": "^1.7.6",
     "axios": "^1.6.7",
     "dotenv": "^16.4.5",
     "expo": "~50.0.11",
@@ -22,6 +23,7 @@
     "react": "18.2.0",
     "react-native": "0.73.4",
     "react-native-maps": "1.10.0",
+    "react-native-modern-datepicker": "^1.0.0-beta.91",
     "react-native-safe-area-context": "4.8.2",
     "react-native-screens": "~3.29.0",
     "react-redux": "^9.1.0",
@@ -30,6 +32,7 @@
   "devDependencies": {
     "@babel/core": "^7.20.0",
     "@types/react": "~18.2.45",
+    "@types/react-native-modern-datepicker": "^1.0.5",
     "typescript": "^5.1.3"
   },
   "private": true