upload 04/08
This commit is contained in:
10
App.tsx
10
App.tsx
@@ -1,9 +1,17 @@
|
|||||||
import React from "react";
|
import React, { useEffect } from "react";
|
||||||
import { NavigationContainer } from "@react-navigation/native";
|
import { NavigationContainer } from "@react-navigation/native";
|
||||||
|
import { Platform } from "react-native";
|
||||||
import { SafeAreaProvider } from "react-native-safe-area-context";
|
import { SafeAreaProvider } from "react-native-safe-area-context";
|
||||||
import AppNavigator from "./src/navigation/AppNavigator";
|
import AppNavigator from "./src/navigation/AppNavigator";
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
|
useEffect(() => {
|
||||||
|
if (Platform.OS === "web") {
|
||||||
|
// Fix scroll bị chặn trên web
|
||||||
|
document.body.style.overflow = "auto";
|
||||||
|
document.documentElement.style.overflow = "auto";
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
return (
|
return (
|
||||||
<SafeAreaProvider>
|
<SafeAreaProvider>
|
||||||
<NavigationContainer>
|
<NavigationContainer>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export default {
|
|||||||
expo: {
|
expo: {
|
||||||
name: "bestpc_mobile",
|
name: "bestpc_mobile",
|
||||||
slug: "bestpc-mobile",
|
slug: "bestpc-mobile",
|
||||||
platforms: ["ios", "android"],
|
platforms: ["ios", "android", "web"],
|
||||||
version: "1.0.0",
|
version: "1.0.0",
|
||||||
orientation: "portrait",
|
orientation: "portrait",
|
||||||
splash: {
|
splash: {
|
||||||
|
|||||||
@@ -14,5 +14,6 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
'react-native-reanimated/plugin'
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
93
package-lock.json
generated
93
package-lock.json
generated
@@ -18,12 +18,13 @@
|
|||||||
"@react-navigation/stack": "^7.3.2",
|
"@react-navigation/stack": "^7.3.2",
|
||||||
"dotenv": "^16.5.0",
|
"dotenv": "^16.5.0",
|
||||||
"expo-image-picker": "~16.1.4",
|
"expo-image-picker": "~16.1.4",
|
||||||
|
"expo-linear-gradient": "~14.1.4",
|
||||||
"expo-status-bar": "~2.2.3",
|
"expo-status-bar": "~2.2.3",
|
||||||
"hermes-engine": "^0.11.0",
|
"hermes-engine": "^0.11.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"metro-runtime": "^0.82.3",
|
"metro-runtime": "^0.82.3",
|
||||||
"react": "19.0.0",
|
"react": "^19.1.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.1.0",
|
||||||
"react-native": "^0.79.2",
|
"react-native": "^0.79.2",
|
||||||
"react-native-collapsible": "^1.6.2",
|
"react-native-collapsible": "^1.6.2",
|
||||||
"react-native-gesture-handler": "~2.24.0",
|
"react-native-gesture-handler": "~2.24.0",
|
||||||
@@ -5203,16 +5204,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/compression": {
|
"node_modules/compression": {
|
||||||
"version": "1.8.0",
|
"version": "1.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz",
|
||||||
"integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==",
|
"integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bytes": "3.1.2",
|
"bytes": "3.1.2",
|
||||||
"compressible": "~2.0.18",
|
"compressible": "~2.0.18",
|
||||||
"debug": "2.6.9",
|
"debug": "2.6.9",
|
||||||
"negotiator": "~0.6.4",
|
"negotiator": "~0.6.4",
|
||||||
"on-headers": "~1.0.2",
|
"on-headers": "~1.1.0",
|
||||||
"safe-buffer": "5.2.1",
|
"safe-buffer": "5.2.1",
|
||||||
"vary": "~1.1.2"
|
"vary": "~1.1.2"
|
||||||
},
|
},
|
||||||
@@ -5999,6 +6000,17 @@
|
|||||||
"react": "*"
|
"react": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/expo-linear-gradient": {
|
||||||
|
"version": "14.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/expo-linear-gradient/-/expo-linear-gradient-14.1.5.tgz",
|
||||||
|
"integrity": "sha512-BSN3MkSGLZoHMduEnAgfhoj3xqcDWaoICgIr4cIYEx1GcHfKMhzA/O4mpZJ/WC27BP1rnAqoKfbclk1eA70ndQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"expo": "*",
|
||||||
|
"react": "*",
|
||||||
|
"react-native": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/expo-modules-autolinking": {
|
"node_modules/expo-modules-autolinking": {
|
||||||
"version": "2.1.10",
|
"version": "2.1.10",
|
||||||
"resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-2.1.10.tgz",
|
"resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-2.1.10.tgz",
|
||||||
@@ -8951,9 +8963,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/on-headers": {
|
"node_modules/on-headers": {
|
||||||
"version": "1.0.2",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
|
||||||
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
|
"integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
@@ -9584,9 +9596,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react": {
|
"node_modules/react": {
|
||||||
"version": "19.0.0",
|
"version": "19.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
|
||||||
"integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==",
|
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
@@ -9624,17 +9636,23 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-dom": {
|
"node_modules/react-dom": {
|
||||||
"version": "19.0.0",
|
"version": "19.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
|
||||||
"integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==",
|
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"scheduler": "^0.25.0"
|
"scheduler": "^0.26.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^19.0.0"
|
"react": "^19.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-dom/node_modules/scheduler": {
|
||||||
|
"version": "0.26.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
|
||||||
|
"integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/react-freeze": {
|
"node_modules/react-freeze": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/react-freeze/-/react-freeze-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-freeze/-/react-freeze-1.0.4.tgz",
|
||||||
@@ -15619,15 +15637,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"compression": {
|
"compression": {
|
||||||
"version": "1.8.0",
|
"version": "1.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz",
|
||||||
"integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==",
|
"integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"bytes": "3.1.2",
|
"bytes": "3.1.2",
|
||||||
"compressible": "~2.0.18",
|
"compressible": "~2.0.18",
|
||||||
"debug": "2.6.9",
|
"debug": "2.6.9",
|
||||||
"negotiator": "~0.6.4",
|
"negotiator": "~0.6.4",
|
||||||
"on-headers": "~1.0.2",
|
"on-headers": "~1.1.0",
|
||||||
"safe-buffer": "5.2.1",
|
"safe-buffer": "5.2.1",
|
||||||
"vary": "~1.1.2"
|
"vary": "~1.1.2"
|
||||||
},
|
},
|
||||||
@@ -16136,6 +16154,12 @@
|
|||||||
"integrity": "sha512-wU9qOnosy4+U4z/o4h8W9PjPvcFMfZXrlUoKTMBW7F4pLqhkkP/5G4EviPZixv4XWFMjn1ExQ5rV6BX8GwJsWA==",
|
"integrity": "sha512-wU9qOnosy4+U4z/o4h8W9PjPvcFMfZXrlUoKTMBW7F4pLqhkkP/5G4EviPZixv4XWFMjn1ExQ5rV6BX8GwJsWA==",
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
|
"expo-linear-gradient": {
|
||||||
|
"version": "14.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/expo-linear-gradient/-/expo-linear-gradient-14.1.5.tgz",
|
||||||
|
"integrity": "sha512-BSN3MkSGLZoHMduEnAgfhoj3xqcDWaoICgIr4cIYEx1GcHfKMhzA/O4mpZJ/WC27BP1rnAqoKfbclk1eA70ndQ==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"expo-modules-autolinking": {
|
"expo-modules-autolinking": {
|
||||||
"version": "2.1.10",
|
"version": "2.1.10",
|
||||||
"resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-2.1.10.tgz",
|
"resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-2.1.10.tgz",
|
||||||
@@ -18140,9 +18164,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"on-headers": {
|
"on-headers": {
|
||||||
"version": "1.0.2",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
|
||||||
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
|
"integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A=="
|
||||||
},
|
},
|
||||||
"once": {
|
"once": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
@@ -18551,9 +18575,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react": {
|
"react": {
|
||||||
"version": "19.0.0",
|
"version": "19.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
|
||||||
"integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ=="
|
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="
|
||||||
},
|
},
|
||||||
"react-devtools-core": {
|
"react-devtools-core": {
|
||||||
"version": "6.1.2",
|
"version": "6.1.2",
|
||||||
@@ -18573,11 +18597,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-dom": {
|
"react-dom": {
|
||||||
"version": "19.0.0",
|
"version": "19.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
|
||||||
"integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==",
|
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"scheduler": "^0.25.0"
|
"scheduler": "^0.26.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"scheduler": {
|
||||||
|
"version": "0.26.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
|
||||||
|
"integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-freeze": {
|
"react-freeze": {
|
||||||
|
|||||||
@@ -24,8 +24,8 @@
|
|||||||
"hermes-engine": "^0.11.0",
|
"hermes-engine": "^0.11.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"metro-runtime": "^0.82.3",
|
"metro-runtime": "^0.82.3",
|
||||||
"react": "19.0.0",
|
"react": "^19.1.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.1.0",
|
||||||
"react-native": "^0.79.2",
|
"react-native": "^0.79.2",
|
||||||
"react-native-collapsible": "^1.6.2",
|
"react-native-collapsible": "^1.6.2",
|
||||||
"react-native-gesture-handler": "~2.24.0",
|
"react-native-gesture-handler": "~2.24.0",
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ export function CreateBuildpc() {
|
|||||||
</View>
|
</View>
|
||||||
{/* Popup */}
|
{/* Popup */}
|
||||||
{showPopup && (
|
{showPopup && (
|
||||||
<PopupBuildpc visible={showPopup} onClose={() => setShowPopup(false)} />
|
<PopupBuildpc show={showPopup} onClose={() => setShowPopup(false)} />
|
||||||
)}
|
)}
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
ScrollView,
|
Platform,
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import { NavigationProp } from "@react-navigation/native";
|
import { NavigationProp } from "@react-navigation/native";
|
||||||
@@ -33,7 +33,7 @@ const ButtonFooter: React.FC<Props> = ({ navigation, activeTab }) => {
|
|||||||
onPress={() => navigation?.navigate(tab.route)}
|
onPress={() => navigation?.navigate(tab.route)}
|
||||||
>
|
>
|
||||||
<Ionicons
|
<Ionicons
|
||||||
name={tab.icon}
|
name={tab?.icon}
|
||||||
size={22}
|
size={22}
|
||||||
color={isActive ? "#4a00e0" : "#666"}
|
color={isActive ? "#4a00e0" : "#666"}
|
||||||
/>
|
/>
|
||||||
@@ -54,9 +54,15 @@ const styles = StyleSheet.create({
|
|||||||
backgroundColor: "#fff",
|
backgroundColor: "#fff",
|
||||||
paddingVertical: 8,
|
paddingVertical: 8,
|
||||||
justifyContent: "space-around",
|
justifyContent: "space-around",
|
||||||
position: "absolute",
|
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
width: "100%",
|
width: "100%",
|
||||||
|
...Platform.select({
|
||||||
|
web: { position: "fixed" as any },
|
||||||
|
default: {
|
||||||
|
position: "absolute",
|
||||||
|
},
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
navItem: {
|
navItem: {
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
|
|||||||
@@ -82,7 +82,8 @@ const Footer: React.FC<Props> = ({ navigation, activeTab = "homepage" }) => {
|
|||||||
/>
|
/>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<Collapsible collapsed={activeIndex !== index}>
|
<Collapsible collapsed={activeIndex !== index}>
|
||||||
{item.links?.map((link, linkIndex) => (
|
{item.links && item.links.length > 0 ? (
|
||||||
|
item.links.map((link, linkIndex) => (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
key={linkIndex}
|
key={linkIndex}
|
||||||
style={styles.linkItem}
|
style={styles.linkItem}
|
||||||
@@ -90,7 +91,10 @@ const Footer: React.FC<Props> = ({ navigation, activeTab = "homepage" }) => {
|
|||||||
>
|
>
|
||||||
<Text style={styles.linkText}>{link.title}</Text>
|
<Text style={styles.linkText}>{link.title}</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
))}
|
))
|
||||||
|
) : item.content ? (
|
||||||
|
<Text style={styles.accordionContent}>{item.content}</Text>
|
||||||
|
) : null}
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
TextInput,
|
TextInput,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
Dimensions,
|
Dimensions,
|
||||||
|
Platform,
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import { Picker } from "@react-native-picker/picker";
|
import { Picker } from "@react-native-picker/picker";
|
||||||
@@ -14,25 +15,15 @@ import { NavigationContainer } from "@react-navigation/native";
|
|||||||
import { createDrawerNavigator } from "@react-navigation/drawer";
|
import { createDrawerNavigator } from "@react-navigation/drawer";
|
||||||
import { createNativeStackNavigator } from "@react-navigation/native-stack";
|
import { createNativeStackNavigator } from "@react-navigation/native-stack";
|
||||||
|
|
||||||
const Header = () => {
|
const Header = ({ toggleMenu }: { toggleMenu: () => void }) => {
|
||||||
const [openSort, setopenSort] = useState(true);
|
const [openSort, setopenSort] = useState(true);
|
||||||
const [openFilter, setOpenFilter] = useState(false);
|
const [openFilter, setOpenFilter] = useState(false);
|
||||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
|
||||||
|
|
||||||
const toggleDrawer = () => {
|
|
||||||
//Props to open/close the drawer
|
|
||||||
setIsMenuOpen(!isMenuOpen);
|
|
||||||
};
|
|
||||||
|
|
||||||
const closeMenu = () => {
|
|
||||||
setIsMenuOpen(false); // Đóng menu
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View>
|
<View style={styles.BoxHeader}>
|
||||||
<View style={styles.headerTop}>
|
<View style={styles.headerTop}>
|
||||||
<View style={styles.headerTopLeft}>
|
<View style={styles.headerTopLeft}>
|
||||||
<TouchableOpacity onPress={toggleDrawer} style={styles.menu}>
|
<TouchableOpacity onPress={toggleMenu} style={styles.menu}>
|
||||||
<Ionicons style={styles.iconMenu} name="menu-outline" size={30} />
|
<Ionicons style={styles.iconMenu} name="menu-outline" size={30} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<Image
|
<Image
|
||||||
@@ -58,6 +49,20 @@ const Header = () => {
|
|||||||
name="caret-down-outline"
|
name="caret-down-outline"
|
||||||
/>
|
/>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View style={styles.searchContainer}>
|
||||||
|
<View style={styles.searchBar}>
|
||||||
|
<TextInput
|
||||||
|
style={styles.searchInput}
|
||||||
|
placeholder="Tìm kiếm khuyến mãi, cửa hàng, sản phẩm,..."
|
||||||
|
placeholderTextColor="rgba(169, 169, 169, 1)"
|
||||||
|
/>
|
||||||
|
<Ionicons style={styles.iconMenu} name="search-outline" size={19} />
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
<View
|
<View
|
||||||
style={
|
style={
|
||||||
openSort
|
openSort
|
||||||
@@ -68,12 +73,7 @@ const Header = () => {
|
|||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[styles.boxSortItem, styles.boxSortItemActive]}
|
style={[styles.boxSortItem, styles.boxSortItemActive]}
|
||||||
>
|
>
|
||||||
<Text
|
<Text style={[styles.boxSortItemText, styles.boxSortItemTextActive]}>
|
||||||
style={[
|
|
||||||
styles.boxSortItemText,
|
|
||||||
styles.boxSortItemTextActive,
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
Hà nội
|
Hà nội
|
||||||
</Text>
|
</Text>
|
||||||
<Ionicons
|
<Ionicons
|
||||||
@@ -89,115 +89,34 @@ const Header = () => {
|
|||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
<View style={styles.searchContainer}>
|
|
||||||
<View style={styles.searchBar}>
|
|
||||||
<TextInput
|
|
||||||
style={styles.searchInput}
|
|
||||||
placeholder="Tìm kiếm khuyến mãi, cửa hàng, sản phẩm,..."
|
|
||||||
placeholderTextColor="rgba(169, 169, 169, 1)"
|
|
||||||
/>
|
|
||||||
<Ionicons style={styles.iconMenu} name="search-outline" size={19} />
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
{isMenuOpen && (
|
|
||||||
<View style={styles.overlay} onTouchStart={closeMenu}>
|
|
||||||
<MainMenu closeMenu={closeMenu} />
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const MainMenu = ({ closeMenu }: { closeMenu: () => void }) => {
|
|
||||||
return (
|
|
||||||
<View style={styles.menuFixed}>
|
|
||||||
<View style={styles.headerMenu}>
|
|
||||||
<Text style={styles.headerMenuText}>Menu</Text>
|
|
||||||
<TouchableOpacity onPress={closeMenu}>
|
|
||||||
<Ionicons style={styles.closeMenu} name="close-outline" size={20} />
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
<View>
|
|
||||||
<TouchableOpacity style={styles.itemMenu}>
|
|
||||||
<View style={styles.textLeft}>
|
|
||||||
<Image
|
|
||||||
source={require("../../../assets/images/icon_buildpc.png")}
|
|
||||||
style={styles.iconBuild}
|
|
||||||
/>
|
|
||||||
<Text style={styles.itemText}>Build PC</Text>
|
|
||||||
</View>
|
|
||||||
<Ionicons name="chevron-forward-outline" size={20} />
|
|
||||||
</TouchableOpacity>
|
|
||||||
<TouchableOpacity style={styles.itemMenu}>
|
|
||||||
<View style={styles.textLeft}>
|
|
||||||
<Image
|
|
||||||
source={require("../../../assets/images/icon_store.png")}
|
|
||||||
style={styles.iconStore}
|
|
||||||
/>
|
|
||||||
<Text style={styles.itemText}>Tìm người bán</Text>
|
|
||||||
</View>
|
|
||||||
<Ionicons name="chevron-forward-outline" size={20} />
|
|
||||||
</TouchableOpacity>
|
|
||||||
<TouchableOpacity style={styles.itemMenu}>
|
|
||||||
<View style={styles.textLeft}>
|
|
||||||
<Image
|
|
||||||
source={require("../../../assets/images/icon_location.png")}
|
|
||||||
style={styles.iconLocation}
|
|
||||||
/>
|
|
||||||
<Text style={styles.itemText}>Địa chỉ sửa chữa</Text>
|
|
||||||
</View>
|
|
||||||
<Ionicons name="chevron-forward-outline" size={20} />
|
|
||||||
</TouchableOpacity>
|
|
||||||
<TouchableOpacity style={styles.itemMenu}>
|
|
||||||
<View style={styles.textLeft}>
|
|
||||||
<Image
|
|
||||||
source={require("../../../assets/images/icon_question.png")}
|
|
||||||
style={styles.iconProduct}
|
|
||||||
/>
|
|
||||||
<Text style={styles.itemText}>Hỏi đáp</Text>
|
|
||||||
</View>
|
|
||||||
<Ionicons name="chevron-forward-outline" size={20} />
|
|
||||||
</TouchableOpacity>
|
|
||||||
<TouchableOpacity style={styles.itemMenu}>
|
|
||||||
<View style={styles.textLeft}>
|
|
||||||
<Image
|
|
||||||
source={require("../../../assets/images/icon_box.png")}
|
|
||||||
style={styles.iconBox}
|
|
||||||
/>
|
|
||||||
<Text style={styles.itemText}>Sản phẩm đã lưu</Text>
|
|
||||||
</View>
|
|
||||||
<Ionicons name="chevron-forward-outline" size={20} />
|
|
||||||
</TouchableOpacity>
|
|
||||||
<TouchableOpacity style={styles.itemMenu}>
|
|
||||||
<View style={styles.textLeft}>
|
|
||||||
<Image
|
|
||||||
source={require("../../../assets/images/icon_article.png")}
|
|
||||||
style={styles.iconArticle}
|
|
||||||
/>
|
|
||||||
<Text style={styles.itemText}>Tin rao vặt</Text>
|
|
||||||
</View>
|
|
||||||
<Ionicons name="chevron-forward-outline" size={20} />
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export { MainMenu };
|
|
||||||
|
|
||||||
let winWidth = Dimensions.get("window").width; //full width
|
let winWidth = Dimensions.get("window").width; //full width
|
||||||
let winHeight = Dimensions.get("window").height; //full height
|
let winHeight = Dimensions.get("window").height; //full height
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
|
BoxHeader: {
|
||||||
|
width: winWidth,
|
||||||
|
zIndex: 9999,
|
||||||
|
...Platform.select({
|
||||||
|
web: {
|
||||||
|
position: "fixed" as any,
|
||||||
|
top: 0,
|
||||||
|
},
|
||||||
|
default: {
|
||||||
|
position: "absolute",
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
headerTop: {
|
headerTop: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
width: winWidth,
|
width: winWidth,
|
||||||
paddingRight: 10,
|
paddingRight: 10,
|
||||||
paddingLeft: 10,
|
paddingLeft: 10,
|
||||||
paddingTop: 45,
|
paddingTop: 10,
|
||||||
paddingBottom: 10,
|
paddingBottom: 10,
|
||||||
backgroundColor: "#462F91",
|
backgroundColor: "#462F91",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
@@ -212,12 +131,14 @@ const styles = StyleSheet.create({
|
|||||||
height: 20,
|
height: 20,
|
||||||
},
|
},
|
||||||
searchContainer: {
|
searchContainer: {
|
||||||
width: winWidth,
|
width: "100%",
|
||||||
paddingLeft: 10,
|
paddingLeft: 10,
|
||||||
paddingRight: 10,
|
paddingRight: 10,
|
||||||
paddingTop: 9,
|
paddingTop: 9,
|
||||||
paddingBottom: 9,
|
paddingBottom: 9,
|
||||||
backgroundColor: "#462F91",
|
backgroundColor: "#462F91",
|
||||||
|
position: "relative",
|
||||||
|
zIndex: 1,
|
||||||
},
|
},
|
||||||
searchBar: {
|
searchBar: {
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
@@ -236,9 +157,8 @@ const styles = StyleSheet.create({
|
|||||||
color: "#000000",
|
color: "#000000",
|
||||||
height: 32,
|
height: 32,
|
||||||
lineHeight: 32,
|
lineHeight: 32,
|
||||||
paddingTop: 7,
|
|
||||||
marginTop: 5,
|
|
||||||
width: winWidth - 60,
|
width: winWidth - 60,
|
||||||
|
outlineWidth: 0,
|
||||||
},
|
},
|
||||||
searchIconContainer: {
|
searchIconContainer: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
@@ -270,6 +190,7 @@ const styles = StyleSheet.create({
|
|||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
gap: 5,
|
gap: 5,
|
||||||
color: "#FFFFFF",
|
color: "#FFFFFF",
|
||||||
|
position: "relative",
|
||||||
},
|
},
|
||||||
textLocation: {
|
textLocation: {
|
||||||
color: "#FFFFFF",
|
color: "#FFFFFF",
|
||||||
@@ -298,10 +219,15 @@ const styles = StyleSheet.create({
|
|||||||
paddingHorizontal: 10,
|
paddingHorizontal: 10,
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
right: 0,
|
right: 0,
|
||||||
top: 40,
|
top: 60,
|
||||||
display: "none",
|
display: "none",
|
||||||
backgroundColor: "#fff",
|
backgroundColor: "#fff",
|
||||||
width: 100,
|
width: 100,
|
||||||
|
zIndex: 999,
|
||||||
|
shadowColor: "#000",
|
||||||
|
shadowOffset: { width: 0, height: 4 },
|
||||||
|
shadowOpacity: 0.3,
|
||||||
|
shadowRadius: 5,
|
||||||
},
|
},
|
||||||
boxSortListActive: {
|
boxSortListActive: {
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@@ -343,91 +269,5 @@ const styles = StyleSheet.create({
|
|||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
},
|
},
|
||||||
headerMenu: {
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
flexDirection: "row",
|
|
||||||
padding: 10,
|
|
||||||
backgroundColor: "#462F91",
|
|
||||||
paddingTop: 55,
|
|
||||||
},
|
|
||||||
menuFixed: {
|
|
||||||
position: "absolute",
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
width: "85%",
|
|
||||||
height: winHeight,
|
|
||||||
backgroundColor: "#fff",
|
|
||||||
zIndex: 99,
|
|
||||||
},
|
|
||||||
headerMenuText: {
|
|
||||||
color: "#fff",
|
|
||||||
fontWeight: "bold",
|
|
||||||
},
|
|
||||||
closeMenu: {
|
|
||||||
backgroundColor: "#fff",
|
|
||||||
width: 20,
|
|
||||||
height: 20,
|
|
||||||
color: "#000",
|
|
||||||
borderRadius: 50,
|
|
||||||
},
|
|
||||||
iconBuild: {
|
|
||||||
width: 30,
|
|
||||||
objectFit: "contain",
|
|
||||||
},
|
|
||||||
iconStore: {
|
|
||||||
width: 20,
|
|
||||||
objectFit: "contain",
|
|
||||||
},
|
|
||||||
iconCart: {
|
|
||||||
width: 20,
|
|
||||||
objectFit: "contain",
|
|
||||||
},
|
|
||||||
iconProduct: {
|
|
||||||
width: 20,
|
|
||||||
objectFit: "contain",
|
|
||||||
},
|
|
||||||
iconArticle: {
|
|
||||||
width: 20,
|
|
||||||
objectFit: "contain",
|
|
||||||
},
|
|
||||||
iconLocation: {
|
|
||||||
width: 20,
|
|
||||||
objectFit: "contain",
|
|
||||||
},
|
|
||||||
iconBox: {
|
|
||||||
width: 20,
|
|
||||||
objectFit: "contain",
|
|
||||||
},
|
|
||||||
itemMenu: {
|
|
||||||
alignItems: "center",
|
|
||||||
flexDirection: "row",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
gap: 10,
|
|
||||||
marginBottom: 10,
|
|
||||||
padding: 10,
|
|
||||||
borderBottomWidth: 1,
|
|
||||||
borderBottomColor: "#e1e1e1",
|
|
||||||
height: 50,
|
|
||||||
},
|
|
||||||
itemText: {
|
|
||||||
fontSize: 15,
|
|
||||||
color: "#000",
|
|
||||||
fontWeight: "bold",
|
|
||||||
},
|
|
||||||
textLeft: {
|
|
||||||
alignItems: "center",
|
|
||||||
gap: 10,
|
|
||||||
flexDirection: "row",
|
|
||||||
},
|
|
||||||
overlay: {
|
|
||||||
position: "absolute",
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
bottom: 0,
|
|
||||||
backgroundColor: "rgba(0, 0, 0, 0.5)", // Lớp nền bán trong suốt
|
|
||||||
zIndex: 98, // Đảm bảo lớp overlay nằm dưới menu
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
export default Header;
|
export default Header;
|
||||||
|
|||||||
197
src/components/header/Menu.tsx
Normal file
197
src/components/header/Menu.tsx
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
import React, { useState, useRef, useEffect } from "react";
|
||||||
|
import {
|
||||||
|
View,
|
||||||
|
StyleSheet,
|
||||||
|
Image,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
TouchableOpacity,
|
||||||
|
Dimensions,
|
||||||
|
Modal,
|
||||||
|
Animated,
|
||||||
|
} from "react-native";
|
||||||
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
|
|
||||||
|
const MainMenu = ({
|
||||||
|
visible,
|
||||||
|
onClose,
|
||||||
|
}: {
|
||||||
|
visible: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}) => {
|
||||||
|
const [internalVisible, setInternalVisible] = useState(visible);
|
||||||
|
const slideAnim = useRef(new Animated.Value(-winWidth * 0.85)).current;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (visible) {
|
||||||
|
setInternalVisible(true);
|
||||||
|
Animated.timing(slideAnim, {
|
||||||
|
toValue: 0,
|
||||||
|
duration: 250,
|
||||||
|
useNativeDriver: false,
|
||||||
|
}).start();
|
||||||
|
} else {
|
||||||
|
Animated.timing(slideAnim, {
|
||||||
|
toValue: -winWidth * 0.85,
|
||||||
|
duration: 250,
|
||||||
|
useNativeDriver: false,
|
||||||
|
}).start(() => {
|
||||||
|
setInternalVisible(false); // Tắt Modal sau animation
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [visible]);
|
||||||
|
|
||||||
|
if (!internalVisible) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal visible={internalVisible} transparent animationType="none">
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.overlay}
|
||||||
|
onPress={onClose}
|
||||||
|
activeOpacity={1}
|
||||||
|
/>
|
||||||
|
<Animated.View style={[styles.menuFixed, { left: slideAnim }]}>
|
||||||
|
<View style={styles.headerMenu}>
|
||||||
|
<Text style={styles.headerMenuText}>Menu</Text>
|
||||||
|
<TouchableOpacity onPress={onClose}>
|
||||||
|
<Ionicons name="close-outline" size={20} style={styles.closeMenu} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
<View>
|
||||||
|
<TouchableOpacity style={styles.itemMenu}>
|
||||||
|
<View style={styles.textLeft}>
|
||||||
|
<Image
|
||||||
|
source={require("../../../assets/images/icon_buildpc.png")}
|
||||||
|
style={styles.iconFix}
|
||||||
|
resizeMode="contain"
|
||||||
|
/>
|
||||||
|
<Text style={styles.itemText}>Build PC</Text>
|
||||||
|
</View>
|
||||||
|
<Ionicons name="chevron-forward-outline" size={20} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
<TouchableOpacity style={styles.itemMenu}>
|
||||||
|
<View style={styles.textLeft}>
|
||||||
|
<Image
|
||||||
|
source={require("../../../assets/images/icon_store.png")}
|
||||||
|
style={styles.iconFix}
|
||||||
|
resizeMode="contain"
|
||||||
|
/>
|
||||||
|
<Text style={styles.itemText}>Tìm người bán</Text>
|
||||||
|
</View>
|
||||||
|
<Ionicons name="chevron-forward-outline" size={20} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
<TouchableOpacity style={styles.itemMenu}>
|
||||||
|
<View style={styles.textLeft}>
|
||||||
|
<Image
|
||||||
|
source={require("../../../assets/images/icon_location.png")}
|
||||||
|
style={styles.iconFix}
|
||||||
|
resizeMode="contain"
|
||||||
|
/>
|
||||||
|
<Text style={styles.itemText}>Địa chỉ sửa chữa</Text>
|
||||||
|
</View>
|
||||||
|
<Ionicons name="chevron-forward-outline" size={20} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
<TouchableOpacity style={styles.itemMenu}>
|
||||||
|
<View style={styles.textLeft}>
|
||||||
|
<Image
|
||||||
|
source={require("../../../assets/images/icon_question.png")}
|
||||||
|
style={styles.iconFix}
|
||||||
|
resizeMode="contain"
|
||||||
|
/>
|
||||||
|
<Text style={styles.itemText}>Hỏi đáp</Text>
|
||||||
|
</View>
|
||||||
|
<Ionicons name="chevron-forward-outline" size={20} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
<TouchableOpacity style={styles.itemMenu}>
|
||||||
|
<View style={styles.textLeft}>
|
||||||
|
<Image
|
||||||
|
source={require("../../../assets/images/icon_box.png")}
|
||||||
|
style={styles.iconFix}
|
||||||
|
resizeMode="contain"
|
||||||
|
/>
|
||||||
|
<Text style={styles.itemText}>Sản phẩm đã lưu</Text>
|
||||||
|
</View>
|
||||||
|
<Ionicons name="chevron-forward-outline" size={20} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
<TouchableOpacity style={styles.itemMenu}>
|
||||||
|
<View style={styles.textLeft}>
|
||||||
|
<Image
|
||||||
|
source={require("../../../assets/images/icon_article.png")}
|
||||||
|
style={styles.iconFix}
|
||||||
|
resizeMode="contain"
|
||||||
|
/>
|
||||||
|
<Text style={styles.itemText}>Tin rao vặt</Text>
|
||||||
|
</View>
|
||||||
|
<Ionicons name="chevron-forward-outline" size={20} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</Animated.View>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { MainMenu };
|
||||||
|
|
||||||
|
let winWidth = Dimensions.get("window").width; //full width
|
||||||
|
let winHeight = Dimensions.get("window").height; //full height
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
headerMenu: {
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
flexDirection: "row",
|
||||||
|
padding: 10,
|
||||||
|
backgroundColor: "#462F91",
|
||||||
|
},
|
||||||
|
menuFixed: {
|
||||||
|
width: "85%",
|
||||||
|
height: "100%",
|
||||||
|
backgroundColor: "#fff",
|
||||||
|
zIndex: 999,
|
||||||
|
},
|
||||||
|
headerMenuText: {
|
||||||
|
color: "#fff",
|
||||||
|
fontWeight: "bold",
|
||||||
|
},
|
||||||
|
closeMenu: {
|
||||||
|
backgroundColor: "#fff",
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
color: "#000",
|
||||||
|
borderRadius: 50,
|
||||||
|
},
|
||||||
|
iconFix: {
|
||||||
|
width: 25,
|
||||||
|
resizeMode: "contain",
|
||||||
|
},
|
||||||
|
itemMenu: {
|
||||||
|
alignItems: "center",
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
gap: 10,
|
||||||
|
marginBottom: 10,
|
||||||
|
padding: 10,
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: "#e1e1e1",
|
||||||
|
height: 50,
|
||||||
|
},
|
||||||
|
itemText: {
|
||||||
|
fontSize: 15,
|
||||||
|
color: "#000",
|
||||||
|
fontWeight: "bold",
|
||||||
|
},
|
||||||
|
textLeft: {
|
||||||
|
alignItems: "center",
|
||||||
|
gap: 10,
|
||||||
|
flexDirection: "row",
|
||||||
|
},
|
||||||
|
overlay: {
|
||||||
|
position: "absolute",
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
backgroundColor: "rgba(0, 0, 0, 0.5)", // Lớp nền bán trong suốt
|
||||||
|
zIndex: 98, // Đảm bảo lớp overlay nằm dưới menu
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -99,7 +99,7 @@ const styles = StyleSheet.create({
|
|||||||
backgroundColor: "#fff",
|
backgroundColor: "#fff",
|
||||||
},
|
},
|
||||||
productImage: {
|
productImage: {
|
||||||
width: winWidth / 2 - 44,
|
width: "100%",
|
||||||
height: 150,
|
height: 150,
|
||||||
position: "relative",
|
position: "relative",
|
||||||
marginBottom: 5,
|
marginBottom: 5,
|
||||||
@@ -112,8 +112,8 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
boxSaleoff: {
|
boxSaleoff: {
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
right: 10,
|
right: 0,
|
||||||
top: 10,
|
top: 0,
|
||||||
width: 35,
|
width: 35,
|
||||||
height: 35,
|
height: 35,
|
||||||
borderRadius: 25,
|
borderRadius: 25,
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ const ItemProductSave = ({ product }: { product: any }) => {
|
|||||||
source={require("../../../assets/images/icon_heart.png")}
|
source={require("../../../assets/images/icon_heart.png")}
|
||||||
style={styles.imgsave}
|
style={styles.imgsave}
|
||||||
alt="icon-heart"
|
alt="icon-heart"
|
||||||
|
resizeMode="contain"
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
@@ -74,10 +75,10 @@ const ItemProductSave = ({ product }: { product: any }) => {
|
|||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
productItem: {
|
productItem: {
|
||||||
width: winWidth / 2 - 20,
|
width: winWidth / 2 - 15,
|
||||||
},
|
},
|
||||||
productImage: {
|
productImage: {
|
||||||
width: winWidth / 2 - 20,
|
width: "100%",
|
||||||
height: 200,
|
height: 200,
|
||||||
position: "relative",
|
position: "relative",
|
||||||
marginBottom: 5,
|
marginBottom: 5,
|
||||||
@@ -85,6 +86,7 @@ const styles = StyleSheet.create({
|
|||||||
borderColor: "#d3d3d3",
|
borderColor: "#d3d3d3",
|
||||||
backgroundColor: "#fff",
|
backgroundColor: "#fff",
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
|
overflow: "hidden",
|
||||||
},
|
},
|
||||||
productImg: {
|
productImg: {
|
||||||
width: "100%",
|
width: "100%",
|
||||||
|
|||||||
@@ -1,12 +1,24 @@
|
|||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import { View, StyleSheet, ScrollView } from "react-native";
|
import {
|
||||||
|
View,
|
||||||
|
StyleSheet,
|
||||||
|
ScrollView,
|
||||||
|
Dimensions,
|
||||||
|
Platform,
|
||||||
|
} from "react-native";
|
||||||
import {
|
import {
|
||||||
useNavigation,
|
useNavigation,
|
||||||
NavigationProp,
|
NavigationProp,
|
||||||
NavigationContainer,
|
NavigationContainer,
|
||||||
} from "@react-navigation/native";
|
} from "@react-navigation/native";
|
||||||
|
import { SafeAreaView } from "react-native-safe-area-context";
|
||||||
import Header from "../components/header/Header";
|
import Header from "../components/header/Header";
|
||||||
|
import { MainMenu } from "@components/header/Menu";
|
||||||
|
import Footer from "../components/footer/Footer";
|
||||||
|
|
||||||
import ButtonFooter from "../components/footer/ButtonFooter";
|
import ButtonFooter from "../components/footer/ButtonFooter";
|
||||||
|
var winWidth = Dimensions.get("window").width; //full width
|
||||||
|
const winHeight = Dimensions.get("window").height;
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
@@ -15,27 +27,49 @@ type Props = {
|
|||||||
|
|
||||||
const AppLayout = ({ children, activeTab }: Props) => {
|
const AppLayout = ({ children, activeTab }: Props) => {
|
||||||
const navigation = useNavigation<NavigationProp<any>>();
|
const navigation = useNavigation<NavigationProp<any>>();
|
||||||
|
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<SafeAreaView style={styles.wrapper}>
|
||||||
<Header />
|
<Header toggleMenu={() => setIsMenuOpen(true)} />
|
||||||
<View style={styles.content}>{children}</View>
|
<MainMenu visible={isMenuOpen} onClose={() => setIsMenuOpen(false)} />
|
||||||
|
<ScrollView
|
||||||
|
style={styles.scroll}
|
||||||
|
contentContainerStyle={styles.scrollContainer}
|
||||||
|
keyboardShouldPersistTaps="handled"
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
<Footer navigation={navigation} />
|
||||||
<ButtonFooter navigation={navigation} activeTab={activeTab} />
|
<ButtonFooter navigation={navigation} activeTab={activeTab} />
|
||||||
</View>
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AppLayout;
|
export default AppLayout;
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
wrapper: {
|
||||||
marginLeft: "auto",
|
|
||||||
marginRight: "auto",
|
|
||||||
maxWidth: 480,
|
|
||||||
flex: 1,
|
flex: 1,
|
||||||
backgroundColor: "#fff",
|
backgroundColor: "#fff",
|
||||||
},
|
},
|
||||||
content: {
|
scroll: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
...Platform.select({
|
||||||
|
web: { marginTop: 113 },
|
||||||
|
default: { marginTop: 0 },
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
scrollWrapper: {
|
||||||
|
flex: 1,
|
||||||
|
overflow: "scroll",
|
||||||
|
},
|
||||||
|
scrollContainer: {
|
||||||
|
flexGrow: 1,
|
||||||
|
},
|
||||||
|
webScrollArea: {
|
||||||
|
flex: 1,
|
||||||
|
overflow: "auto" as any,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ const AppNavigator: React.FC = () => {
|
|||||||
<Stack.Screen name="buildpc" component={Buildpc} />
|
<Stack.Screen name="buildpc" component={Buildpc} />
|
||||||
<Stack.Screen name="comparebuildpc" component={CompareBuildpc} />
|
<Stack.Screen name="comparebuildpc" component={CompareBuildpc} />
|
||||||
<Stack.Screen name="buildpcdetail" component={BuildpcDeail} />
|
<Stack.Screen name="buildpcdetail" component={BuildpcDeail} />
|
||||||
|
<Stack.Screen name="classifieds" component={BuildpcDeail} />
|
||||||
</Stack.Navigator>
|
</Stack.Navigator>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// src/screens/HomeScreen.tsx
|
// src/screens/HomeScreen.tsx
|
||||||
import React, { useState } from "react";
|
import React, { useState, useRef } from "react";
|
||||||
import { useNavigation, NavigationProp } from "@react-navigation/native";
|
import { useNavigation, NavigationProp } from "@react-navigation/native";
|
||||||
import {
|
import {
|
||||||
View,
|
View,
|
||||||
@@ -11,9 +11,9 @@ import {
|
|||||||
ImageBackground,
|
ImageBackground,
|
||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
SafeAreaView,
|
SafeAreaView,
|
||||||
|
ScrollViewBase,
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
import { globalStyles } from "../styles/globalStyles";
|
import { globalStyles } from "../styles/globalStyles";
|
||||||
import Swiper from "react-native-swiper";
|
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import { products } from "../data/product";
|
import { products } from "../data/product";
|
||||||
import ProductItem from "../components/product/ItemProduct";
|
import ProductItem from "../components/product/ItemProduct";
|
||||||
@@ -23,17 +23,19 @@ import chunk from "lodash/chunk";
|
|||||||
import { globalAgent } from "http";
|
import { globalAgent } from "http";
|
||||||
import { WebView } from "react-native-webview";
|
import { WebView } from "react-native-webview";
|
||||||
import AppLayout from "../layouts/AppLayout";
|
import AppLayout from "../layouts/AppLayout";
|
||||||
import Footer from "../components/footer/Footer";
|
import Carousel, { ICarouselInstance } from "react-native-reanimated-carousel";
|
||||||
|
|
||||||
var winWidth = Dimensions.get("window").width; //full width
|
var winWidth = Dimensions.get("window").width; //full width
|
||||||
var winHeight = Dimensions.get("window").height; //full height
|
var winHeight = Dimensions.get("window").height; //full height
|
||||||
const ratio = winWidth / 930;
|
|
||||||
|
|
||||||
const HomeScreen: React.FC = () => {
|
const HomeScreen: React.FC = () => {
|
||||||
const navigation = useNavigation<NavigationProp<any>>();
|
const navigation = useNavigation<NavigationProp<any>>();
|
||||||
return (
|
return (
|
||||||
<AppLayout activeTab="homepage">
|
<AppLayout activeTab="homepage">
|
||||||
<ScrollView>
|
<ScrollView
|
||||||
|
keyboardShouldPersistTaps="handled"
|
||||||
|
showsVerticalScrollIndicator={true}
|
||||||
|
>
|
||||||
<SliderHome />
|
<SliderHome />
|
||||||
<BoxMenuHome />
|
<BoxMenuHome />
|
||||||
<BoxProductReviewTop />
|
<BoxProductReviewTop />
|
||||||
@@ -45,67 +47,58 @@ const HomeScreen: React.FC = () => {
|
|||||||
<BoxArticleClassifieds />
|
<BoxArticleClassifieds />
|
||||||
<BoxBannerSaleHome />
|
<BoxBannerSaleHome />
|
||||||
<BoxPromotionHome />
|
<BoxPromotionHome />
|
||||||
<Footer navigation={navigation} />
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const SliderHome = () => {
|
const SliderHome = () => {
|
||||||
|
const [currentIndex, setCurrentIndex] = useState(0);
|
||||||
|
const carouselRef = useRef<ICarouselInstance>(null);
|
||||||
|
|
||||||
|
const handleDotPress = (index: number) => {
|
||||||
|
if (carouselRef.current) {
|
||||||
|
carouselRef.current?.scrollTo({ index, animated: true });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const banner_list = [
|
||||||
|
require("../../assets/images/banner_slider.png"),
|
||||||
|
require("../../assets/images/banner_slider.png"),
|
||||||
|
require("../../assets/images/banner_slider.png"),
|
||||||
|
require("../../assets/images/banner_slider.png"),
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.slider}>
|
<View style={styles.slider}>
|
||||||
<Swiper
|
<Carousel
|
||||||
style={styles.sliderSwipper}
|
loop
|
||||||
autoplay={false}
|
autoPlay
|
||||||
autoplayTimeout={3}
|
autoPlayInterval={3000}
|
||||||
showsButtons={true}
|
width={winWidth}
|
||||||
showsPagination={false}
|
height={200}
|
||||||
nextButton={
|
data={banner_list}
|
||||||
<Ionicons
|
scrollAnimationDuration={1000}
|
||||||
name="chevron-forward-outline"
|
onSnapToItem={(index) => {
|
||||||
size={30}
|
setCurrentIndex(index);
|
||||||
color="white"
|
}}
|
||||||
style={styles.buttonNext}
|
renderItem={({ item }) => (
|
||||||
|
<Image source={item} style={styles.imgSlider} resizeMode="cover" />
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
}
|
<View style={globalStyles.dotWrapper}>
|
||||||
prevButton={
|
{banner_list.map((_, index) => (
|
||||||
<Ionicons
|
<TouchableOpacity key={index} onPress={() => handleDotPress(index)}>
|
||||||
name="chevron-back-outline"
|
<View
|
||||||
size={30}
|
key={index}
|
||||||
color="white"
|
style={[
|
||||||
style={styles.buttonPrev}
|
globalStyles.dot,
|
||||||
/>
|
currentIndex === index && globalStyles.activeDot,
|
||||||
}
|
]}
|
||||||
>
|
|
||||||
<View style={styles.imgContainer}>
|
|
||||||
<Image
|
|
||||||
style={styles.imgSlider}
|
|
||||||
source={require("../../assets/images/banner_slider.png")}
|
|
||||||
resizeMode="cover"
|
|
||||||
/>
|
/>
|
||||||
|
</TouchableOpacity>
|
||||||
|
))}
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.imgContainer}>
|
|
||||||
<Image
|
|
||||||
style={styles.imgSlider}
|
|
||||||
source={require("../../assets/images/banner_slider.png")}
|
|
||||||
resizeMode="cover"
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
<View style={styles.imgContainer}>
|
|
||||||
<Image
|
|
||||||
style={styles.imgSlider}
|
|
||||||
source={require("../../assets/images/banner_slider.png")}
|
|
||||||
resizeMode="cover"
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
<View style={styles.imgContainer}>
|
|
||||||
<Image
|
|
||||||
style={styles.imgSlider}
|
|
||||||
source={require("../../assets/images/banner_slider.png")}
|
|
||||||
resizeMode="cover"
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
</Swiper>
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -252,124 +245,132 @@ const BoxProductReviewTop = () => {
|
|||||||
|
|
||||||
const BoxCategoryHome = () => {
|
const BoxCategoryHome = () => {
|
||||||
const chunkedItems = chunk(categories, 8);
|
const chunkedItems = chunk(categories, 8);
|
||||||
|
const [activeIndex, setActiveIndex] = useState(0);
|
||||||
|
const carouselRef = useRef<ICarouselInstance>(null);
|
||||||
|
|
||||||
|
const handleDotPress = (index: number) => {
|
||||||
|
if (carouselRef.current) {
|
||||||
|
carouselRef.current?.scrollTo({ index, animated: true });
|
||||||
|
}
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<View style={globalStyles.boxCategoryHome}>
|
<View style={globalStyles.boxCategoryHome}>
|
||||||
<Text style={globalStyles.textBoxCategoryHome}>Tìm theo danh mục</Text>
|
<Text style={globalStyles.textBoxCategoryHome}>Tìm theo danh mục</Text>
|
||||||
<Swiper
|
<Carousel
|
||||||
style={globalStyles.listCategoryBox}
|
|
||||||
loop={false}
|
loop={false}
|
||||||
showsPagination={true}
|
width={winWidth}
|
||||||
dot={<View style={globalStyles.dot} />}
|
height={300}
|
||||||
activeDot={<View style={globalStyles.activeDot} />}
|
data={chunkedItems}
|
||||||
paginationStyle={{
|
scrollAnimationDuration={500}
|
||||||
bottom: 10,
|
onSnapToItem={(index) => setActiveIndex(index)}
|
||||||
}}
|
renderItem={({ item }) => (
|
||||||
>
|
|
||||||
{chunkedItems.map((group, slideIndex) => (
|
|
||||||
<View
|
<View
|
||||||
key={`slide-${slideIndex}`}
|
|
||||||
style={{
|
style={{
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
flexWrap: "wrap",
|
flexWrap: "wrap",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
paddingHorizontal: 10,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{group.map((item) => (
|
{item.map((cat) => (
|
||||||
<TouchableOpacity key={item.id} style={globalStyles.categoryItem}>
|
<TouchableOpacity key={cat.id} style={globalStyles.categoryItem}>
|
||||||
<View style={globalStyles.boxImageCategory}>
|
<View style={globalStyles.boxImageCategory}>
|
||||||
<Image
|
<Image
|
||||||
source={require("../../assets/images/category-laptop.png")}
|
source={require("../../assets/images/category-laptop.png")}
|
||||||
style={globalStyles.iconCategory}
|
style={globalStyles.iconCategory}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<Text style={globalStyles.categoryName}>{item.name}</Text>
|
<Text style={globalStyles.categoryName}>{cat.name}</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<View style={globalStyles.dotWrapper}>
|
||||||
|
{chunkedItems.map((_, index) => (
|
||||||
|
<TouchableOpacity key={index} onPress={() => handleDotPress(index)}>
|
||||||
|
<View
|
||||||
|
key={index}
|
||||||
|
style={[
|
||||||
|
globalStyles.dot,
|
||||||
|
activeIndex === index && globalStyles.activeDot,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</TouchableOpacity>
|
||||||
))}
|
))}
|
||||||
</Swiper>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const BoxBusiness = () => {
|
const BoxBusiness = () => {
|
||||||
|
const [currentIndex, setCurrentIndex] = useState(0);
|
||||||
|
const carouselRef = useRef<ICarouselInstance>(null);
|
||||||
|
|
||||||
|
const handleDotPress = (index: number) => {
|
||||||
|
if (carouselRef.current) {
|
||||||
|
carouselRef.current?.scrollTo({ index, animated: true });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const slides = [
|
||||||
|
[
|
||||||
|
require("../../assets/images/logo-hacom.png"),
|
||||||
|
require("../../assets/images/logo-hacom.png"),
|
||||||
|
require("../../assets/images/logo-anphat.png"),
|
||||||
|
require("../../assets/images/logo-gearvn.png"),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
require("../../assets/images/logo-hacom.png"),
|
||||||
|
require("../../assets/images/logo-hacom.png"),
|
||||||
|
require("../../assets/images/logo-anphat.png"),
|
||||||
|
require("../../assets/images/logo-gearvn.png"),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={globalStyles.BoxBusiness}>
|
<View style={globalStyles.BoxBusiness}>
|
||||||
<Text style={globalStyles.textBoxBusiness}>DOANH NGHIỆP NỔI BẬT</Text>
|
<Text style={globalStyles.textBoxBusiness}>DOANH NGHIỆP NỔI BẬT</Text>
|
||||||
|
|
||||||
<Swiper
|
<Carousel
|
||||||
style={globalStyles.sliderBusinesses}
|
style={globalStyles.sliderBusinesses}
|
||||||
autoplay={false}
|
loop={false}
|
||||||
showsPagination={true}
|
width={winWidth}
|
||||||
dot={<View style={globalStyles.dot} />}
|
height={45}
|
||||||
activeDot={<View style={globalStyles.activeDot} />}
|
data={slides}
|
||||||
paginationStyle={{
|
scrollAnimationDuration={500}
|
||||||
bottom: 10,
|
onSnapToItem={(index) => setCurrentIndex(index)}
|
||||||
}}
|
renderItem={({ item }) => (
|
||||||
>
|
<View style={{ flexDirection: "row", paddingHorizontal: 10 }}>
|
||||||
|
{item.map((logo, idx) => (
|
||||||
|
<TouchableOpacity key={idx} style={globalStyles.logoItem}>
|
||||||
|
<Image source={logo} style={globalStyles.logoImage} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Custom Dots */}
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
paddingHorizontal: 10,
|
justifyContent: "center",
|
||||||
|
marginTop: 10,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<TouchableOpacity style={globalStyles.logoItem}>
|
{slides.map((_, index) => (
|
||||||
<Image
|
<TouchableOpacity key={index} onPress={() => handleDotPress(index)}>
|
||||||
source={require("../../assets/images/logo-hacom.png")}
|
|
||||||
style={globalStyles.logoImage}
|
|
||||||
/>
|
|
||||||
</TouchableOpacity>
|
|
||||||
<TouchableOpacity style={globalStyles.logoItem}>
|
|
||||||
<Image
|
|
||||||
source={require("../../assets/images/logo-hacom.png")}
|
|
||||||
style={globalStyles.logoImage}
|
|
||||||
/>
|
|
||||||
</TouchableOpacity>
|
|
||||||
<TouchableOpacity style={globalStyles.logoItem}>
|
|
||||||
<Image
|
|
||||||
source={require("../../assets/images/logo-anphat.png")}
|
|
||||||
style={globalStyles.logoImage}
|
|
||||||
/>
|
|
||||||
</TouchableOpacity>
|
|
||||||
<TouchableOpacity style={globalStyles.logoItem}>
|
|
||||||
<Image
|
|
||||||
source={require("../../assets/images/logo-gearvn.png")}
|
|
||||||
style={globalStyles.logoImage}
|
|
||||||
/>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
<View
|
<View
|
||||||
style={{
|
key={index}
|
||||||
flexDirection: "row",
|
style={[
|
||||||
paddingHorizontal: 10,
|
globalStyles.dot,
|
||||||
}}
|
currentIndex === index && globalStyles.activeDot,
|
||||||
>
|
]}
|
||||||
<TouchableOpacity style={globalStyles.logoItem}>
|
|
||||||
<Image
|
|
||||||
source={require("../../assets/images/logo-hacom.png")}
|
|
||||||
style={globalStyles.logoImage}
|
|
||||||
/>
|
|
||||||
</TouchableOpacity>
|
|
||||||
<TouchableOpacity style={globalStyles.logoItem}>
|
|
||||||
<Image
|
|
||||||
source={require("../../assets/images/logo-hacom.png")}
|
|
||||||
style={globalStyles.logoImage}
|
|
||||||
/>
|
|
||||||
</TouchableOpacity>
|
|
||||||
<TouchableOpacity style={globalStyles.logoItem}>
|
|
||||||
<Image
|
|
||||||
source={require("../../assets/images/logo-anphat.png")}
|
|
||||||
style={globalStyles.logoImage}
|
|
||||||
/>
|
|
||||||
</TouchableOpacity>
|
|
||||||
<TouchableOpacity style={globalStyles.logoItem}>
|
|
||||||
<Image
|
|
||||||
source={require("../../assets/images/logo-gearvn.png")}
|
|
||||||
style={globalStyles.logoImage}
|
|
||||||
/>
|
/>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
))}
|
||||||
</View>
|
</View>
|
||||||
</Swiper>
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -738,67 +739,65 @@ const BoxArticleClassifieds = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const BoxBannerSaleHome = () => {
|
const BoxBannerSaleHome = () => {
|
||||||
|
const [activeIndex, setActiveIndex] = useState(0);
|
||||||
|
const carouselRef = useRef<ICarouselInstance>(null);
|
||||||
|
|
||||||
|
const slides = [
|
||||||
|
[
|
||||||
|
require("../../assets/images/banner-sale-1.png"),
|
||||||
|
require("../../assets/images/banner-sale-2.png"),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
require("../../assets/images/banner-sale-2.png"),
|
||||||
|
require("../../assets/images/banner-sale-3.png"),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
require("../../assets/images/banner-sale-3.png"),
|
||||||
|
require("../../assets/images/banner-sale-1.png"),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
const handleDotPress = (index: number) => {
|
||||||
|
if (carouselRef.current) {
|
||||||
|
carouselRef.current.scrollTo({ index, animated: true });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={globalStyles.BoxBannerSaleHome}>
|
<View style={globalStyles.BoxBannerSaleHome}>
|
||||||
<Swiper
|
<Carousel
|
||||||
style={globalStyles.sliderBannerSale}
|
ref={carouselRef}
|
||||||
autoplay={false}
|
width={winWidth}
|
||||||
showsPagination={true}
|
height={220}
|
||||||
dot={<View style={globalStyles.dot} />}
|
data={slides}
|
||||||
activeDot={<View style={globalStyles.activeDot} />}
|
loop
|
||||||
paginationStyle={{
|
autoPlay
|
||||||
bottom: 10,
|
autoPlayInterval={5000}
|
||||||
}}
|
scrollAnimationDuration={600}
|
||||||
>
|
onSnapToItem={(index) => setActiveIndex(index)}
|
||||||
<View style={{ flexDirection: "row" }}>
|
renderItem={({ item }) => (
|
||||||
<TouchableOpacity style={globalStyles.itemBannerSale}>
|
<View style={{ flexDirection: "row", paddingHorizontal: 10 }}>
|
||||||
<Image
|
{item.map((logo, idx) => (
|
||||||
source={require("../../assets/images/banner-sale-1.png")}
|
<TouchableOpacity key={idx} style={globalStyles.itemBannerSale}>
|
||||||
style={globalStyles.imgBannerSale}
|
<Image source={logo} style={globalStyles.imgBannerSale} />
|
||||||
alt="anh tin tuc"
|
|
||||||
/>
|
|
||||||
</TouchableOpacity>
|
|
||||||
<TouchableOpacity style={globalStyles.itemBannerSale}>
|
|
||||||
<Image
|
|
||||||
source={require("../../assets/images/banner-sale-2.png")}
|
|
||||||
style={globalStyles.imgBannerSale}
|
|
||||||
alt="anh tin tuc"
|
|
||||||
/>
|
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
))}
|
||||||
</View>
|
</View>
|
||||||
<View style={{ flexDirection: "row" }}>
|
)}
|
||||||
<TouchableOpacity style={globalStyles.itemBannerSale}>
|
/>
|
||||||
<Image
|
<View style={globalStyles.dotWrapper}>
|
||||||
source={require("../../assets/images/banner-sale-2.png")}
|
{slides.map((_, index) => (
|
||||||
style={globalStyles.imgBannerSale}
|
<TouchableOpacity key={index} onPress={() => handleDotPress(index)}>
|
||||||
alt="anh tin tuc"
|
<View
|
||||||
/>
|
style={
|
||||||
</TouchableOpacity>
|
index === activeIndex
|
||||||
<TouchableOpacity style={globalStyles.itemBannerSale}>
|
? globalStyles.activeDot
|
||||||
<Image
|
: globalStyles.dot
|
||||||
source={require("../../assets/images/banner-sale-3.png")}
|
}
|
||||||
style={globalStyles.imgBannerSale}
|
|
||||||
alt="anh tin tuc"
|
|
||||||
/>
|
/>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
))}
|
||||||
</View>
|
</View>
|
||||||
<View style={{ flexDirection: "row" }}>
|
|
||||||
<TouchableOpacity style={globalStyles.itemBannerSale}>
|
|
||||||
<Image
|
|
||||||
source={require("../../assets/images/banner-sale-3.png")}
|
|
||||||
style={globalStyles.imgBannerSale}
|
|
||||||
alt="anh tin tuc"
|
|
||||||
/>
|
|
||||||
</TouchableOpacity>
|
|
||||||
<TouchableOpacity style={globalStyles.itemBannerSale}>
|
|
||||||
<Image
|
|
||||||
source={require("../../assets/images/banner-sale-1.png")}
|
|
||||||
style={globalStyles.imgBannerSale}
|
|
||||||
alt="anh tin tuc"
|
|
||||||
/>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
</Swiper>
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -899,6 +898,7 @@ const styles = StyleSheet.create({
|
|||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
margin: 10,
|
margin: 10,
|
||||||
|
flex: 1,
|
||||||
},
|
},
|
||||||
sliderSwipper: {
|
sliderSwipper: {
|
||||||
height: 210,
|
height: 210,
|
||||||
@@ -913,11 +913,12 @@ const styles = StyleSheet.create({
|
|||||||
objectFit: "cover",
|
objectFit: "cover",
|
||||||
},
|
},
|
||||||
imgSlider: {
|
imgSlider: {
|
||||||
width: winWidth,
|
width: winWidth - 20,
|
||||||
height: "100%",
|
height: "100%",
|
||||||
objectFit: "cover",
|
objectFit: "cover",
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
|
marginRight: 10,
|
||||||
},
|
},
|
||||||
buttonNext: {
|
buttonNext: {
|
||||||
color: "#fff", // Màu chữ cho các nút chuyển
|
color: "#fff", // Màu chữ cho các nút chuyển
|
||||||
@@ -930,15 +931,19 @@ const styles = StyleSheet.create({
|
|||||||
fontWeight: "bold",
|
fontWeight: "bold",
|
||||||
},
|
},
|
||||||
boxMenuHome: {
|
boxMenuHome: {
|
||||||
|
marginTop: 10,
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
flexWrap: "wrap",
|
flexWrap: "wrap",
|
||||||
|
justifyContent: "flex-start",
|
||||||
marginLeft: 20,
|
marginLeft: 20,
|
||||||
marginRight: 20,
|
gap: 10,
|
||||||
gap: 20,
|
|
||||||
backgroundColor: "#fff",
|
backgroundColor: "#fff",
|
||||||
},
|
},
|
||||||
MenuItem: {
|
MenuItem: {
|
||||||
width: winWidth / 4 - 25,
|
width: winWidth / 4 - 20,
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
},
|
},
|
||||||
boxIconMenu: {
|
boxIconMenu: {
|
||||||
width: 50,
|
width: 50,
|
||||||
@@ -948,14 +953,15 @@ const styles = StyleSheet.create({
|
|||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
backgroundColor: "#eeeeee",
|
backgroundColor: "#eeeeee",
|
||||||
borderRadius: 25,
|
borderRadius: 25,
|
||||||
marginLeft: "auto",
|
flexDirection: "row",
|
||||||
marginRight: "auto",
|
margin: "auto",
|
||||||
},
|
},
|
||||||
textMenu: {
|
textMenu: {
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
color: "#333",
|
color: "#333",
|
||||||
fontWeight: "bold",
|
fontWeight: "bold",
|
||||||
|
height: 35,
|
||||||
},
|
},
|
||||||
iconMenuItem: {
|
iconMenuItem: {
|
||||||
width: 20,
|
width: 20,
|
||||||
|
|||||||
@@ -92,7 +92,6 @@ export default function Buildpc() {
|
|||||||
{/* Buildpc Content */}
|
{/* Buildpc Content */}
|
||||||
<CreateBuildpc />
|
<CreateBuildpc />
|
||||||
</View>
|
</View>
|
||||||
<Footer navigation={navigation} />
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
);
|
);
|
||||||
@@ -136,6 +135,7 @@ const styles = StyleSheet.create({
|
|||||||
marginTop: 12,
|
marginTop: 12,
|
||||||
backgroundColor: "#fff",
|
backgroundColor: "#fff",
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
|
marginBottom: 10,
|
||||||
},
|
},
|
||||||
inputBox: {
|
inputBox: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
|
|||||||
@@ -81,7 +81,6 @@ export function BuildpcDeail() {
|
|||||||
{/* đánh giá */}
|
{/* đánh giá */}
|
||||||
<ReviewList />
|
<ReviewList />
|
||||||
</View>
|
</View>
|
||||||
<Footer navigation={navigation} />
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -58,7 +58,6 @@ export function CompareBuildpc() {
|
|||||||
|
|
||||||
<ListCompare />
|
<ListCompare />
|
||||||
</View>
|
</View>
|
||||||
<Footer navigation={navigation} />
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
);
|
);
|
||||||
|
|||||||
76
src/screens/classifieds/HomeClassifieds.tsx
Normal file
76
src/screens/classifieds/HomeClassifieds.tsx
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import React from "react";
|
||||||
|
import {
|
||||||
|
View,
|
||||||
|
Text,
|
||||||
|
Image,
|
||||||
|
TouchableOpacity,
|
||||||
|
ScrollView,
|
||||||
|
StyleSheet,
|
||||||
|
Dimensions,
|
||||||
|
} from "react-native";
|
||||||
|
const { width } = Dimensions.get("window");
|
||||||
|
import { useNavigation, NavigationProp } from "@react-navigation/native";
|
||||||
|
import Footer from "@components/footer/Footer";
|
||||||
|
import AppLayout from "@layouts/AppLayout";
|
||||||
|
import { Ionicons } from "@expo/vector-icons"; // hoặc icon_2025 nếu bạn có icon font riêng
|
||||||
|
|
||||||
|
export default function ListCompare() {
|
||||||
|
const navigation = useNavigation<NavigationProp<any>>();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AppLayout activeTab="classifieds">
|
||||||
|
<ScrollView>
|
||||||
|
<View style={styles.container}>
|
||||||
|
{/* Breadcrumb */}
|
||||||
|
<View style={styles.breadcrumb}>
|
||||||
|
<View style={styles.breadcrumbItem}>
|
||||||
|
<TouchableOpacity>
|
||||||
|
<Ionicons
|
||||||
|
name="home"
|
||||||
|
size={16}
|
||||||
|
color="#637381"
|
||||||
|
style={styles.icon}
|
||||||
|
/>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<Text style={styles.angle}>›</Text>
|
||||||
|
</View>
|
||||||
|
<View style={styles.breadcrumbItem}>
|
||||||
|
<Text style={styles.text}>Rao vặt</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View style={styles.boxCategory}>
|
||||||
|
<Text>Danh mục</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<Footer navigation={navigation} />
|
||||||
|
</ScrollView>
|
||||||
|
</AppLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
paddingHorizontal: 10,
|
||||||
|
},
|
||||||
|
breadcrumb: {
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
paddingVertical: 12,
|
||||||
|
flexWrap: "wrap",
|
||||||
|
},
|
||||||
|
breadcrumbItem: {
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
marginRight: 8,
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
color: "#000",
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
marginRight: 5,
|
||||||
|
},
|
||||||
|
angle: {
|
||||||
|
marginLeft: 12,
|
||||||
|
color: "#888",
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -24,7 +24,6 @@ import ProductInformation from "./ProductInformation";
|
|||||||
import ProductSpecification from "./ProductSpecification";
|
import ProductSpecification from "./ProductSpecification";
|
||||||
import { products } from "../../data/product";
|
import { products } from "../../data/product";
|
||||||
import ProductItem from "@components/product/ItemProduct";
|
import ProductItem from "@components/product/ItemProduct";
|
||||||
import Footer from "@components/footer/Footer";
|
|
||||||
|
|
||||||
const ProductDetail = () => {
|
const ProductDetail = () => {
|
||||||
const navigation = useNavigation<NavigationProp<any>>();
|
const navigation = useNavigation<NavigationProp<any>>();
|
||||||
@@ -192,7 +191,6 @@ const ProductDetail = () => {
|
|||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<Footer navigation={navigation} />
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
);
|
);
|
||||||
@@ -358,7 +356,7 @@ const styles = StyleSheet.create({
|
|||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
flexWrap: "wrap",
|
flexWrap: "wrap",
|
||||||
gap: 5,
|
gap: 5,
|
||||||
marginLeft: 7,
|
|
||||||
marginTop: 10,
|
marginTop: 10,
|
||||||
|
marginLeft: 7,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -70,7 +70,6 @@ const ProductList = () => {
|
|||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<Footer navigation={navigation} />
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -67,7 +67,6 @@ const ProductListBig = () => {
|
|||||||
showsVerticalScrollIndicator={false}
|
showsVerticalScrollIndicator={false}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<Footer navigation={navigation} />
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export const globalStyles = StyleSheet.create({
|
|||||||
marginBottom: 10,
|
marginBottom: 10,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
flexWrap: 'wrap',
|
flexWrap: 'wrap',
|
||||||
gap: 10
|
gap: 10,
|
||||||
},
|
},
|
||||||
moreAll: {
|
moreAll: {
|
||||||
width: 110,
|
width: 110,
|
||||||
@@ -85,8 +85,11 @@ export const globalStyles = StyleSheet.create({
|
|||||||
height: 300
|
height: 300
|
||||||
},
|
},
|
||||||
categoryItem: {
|
categoryItem: {
|
||||||
width: '25%',
|
width: winWidth / 4 - 15,
|
||||||
marginBottom: 25
|
marginBottom: 25,
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
},
|
},
|
||||||
boxImageCategory: {
|
boxImageCategory: {
|
||||||
width: 70,
|
width: 70,
|
||||||
@@ -99,7 +102,8 @@ export const globalStyles = StyleSheet.create({
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
iconCategory: {
|
iconCategory: {
|
||||||
width: 50,
|
width: 45,
|
||||||
|
height: 45,
|
||||||
objectFit: 'contain',
|
objectFit: 'contain',
|
||||||
},
|
},
|
||||||
categoryName: {
|
categoryName: {
|
||||||
@@ -107,6 +111,15 @@ export const globalStyles = StyleSheet.create({
|
|||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
marginTop: 5,
|
marginTop: 5,
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
|
height: 40
|
||||||
|
},
|
||||||
|
dotWrapper: {
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 10,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'center',
|
||||||
},
|
},
|
||||||
dot: {
|
dot: {
|
||||||
backgroundColor: 'rgba(0,0,0,.2)',
|
backgroundColor: 'rgba(0,0,0,.2)',
|
||||||
@@ -116,11 +129,7 @@ export const globalStyles = StyleSheet.create({
|
|||||||
margin: 3,
|
margin: 3,
|
||||||
},
|
},
|
||||||
activeDot: {
|
activeDot: {
|
||||||
backgroundColor: '#ff7a00',
|
backgroundColor: '#f9ef06',
|
||||||
width: 40,
|
|
||||||
height: 10,
|
|
||||||
borderRadius: 5,
|
|
||||||
margin: 3,
|
|
||||||
},
|
},
|
||||||
BoxBusiness: {
|
BoxBusiness: {
|
||||||
marginTop: 25,
|
marginTop: 25,
|
||||||
@@ -135,10 +144,11 @@ export const globalStyles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
sliderBusinesses: {
|
sliderBusinesses: {
|
||||||
marginTop: 20,
|
marginTop: 20,
|
||||||
height: 85
|
width: winWidth - 10,
|
||||||
|
marginBottom: 10
|
||||||
},
|
},
|
||||||
logoItem: {
|
logoItem: {
|
||||||
width: itemWidth,
|
width: itemWidth - 3,
|
||||||
height: 40,
|
height: 40,
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
backgroundColor: '#fff',
|
backgroundColor: '#fff',
|
||||||
@@ -159,7 +169,7 @@ export const globalStyles = StyleSheet.create({
|
|||||||
marginTop: 20,
|
marginTop: 20,
|
||||||
width: winWidth,
|
width: winWidth,
|
||||||
paddingLeft: 10,
|
paddingLeft: 10,
|
||||||
paddingRight: 10,
|
paddingRight: 10
|
||||||
},
|
},
|
||||||
textBoxProductSaveHome: {
|
textBoxProductSaveHome: {
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
@@ -289,18 +299,13 @@ export const globalStyles = StyleSheet.create({
|
|||||||
imgBannerSale: {
|
imgBannerSale: {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
objectFit: 'cover',
|
resizeMode: 'cover',
|
||||||
borderRadius: 12
|
borderRadius: 12
|
||||||
},
|
},
|
||||||
sliderBannerSale: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
height: 200
|
|
||||||
},
|
|
||||||
itemBannerSale: {
|
itemBannerSale: {
|
||||||
width: winWidth - 50,
|
width: winWidth - 80,
|
||||||
height: 160,
|
height: 180,
|
||||||
objectFit: 'cover',
|
resizeMode: 'cover',
|
||||||
marginRight: 10
|
marginRight: 10
|
||||||
},
|
},
|
||||||
BoxPromotionHome: {
|
BoxPromotionHome: {
|
||||||
|
|||||||
Reference in New Issue
Block a user