Skip to content

Commit

Permalink
seperated search bar into its own component
Browse files Browse the repository at this point in the history
  • Loading branch information
meganleongg committed Mar 7, 2024
1 parent 7df5939 commit 2d13856
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 74 deletions.
2 changes: 1 addition & 1 deletion dfm-sideline-sidekick-app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export default function App() {
<Stack.Screen
name="MedicalConditions"
component={ConditionsSection}
options={{ headerShown: false}}
options={{ headerShown: false }}
/>
</Stack.Navigator>
<BottomNavBarComponent />
Expand Down
57 changes: 57 additions & 0 deletions dfm-sideline-sidekick-app/SearchBarComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React from "react";
import { Text, TextInput, TouchableOpacity, View } from "react-native";
import Icon from "react-native-vector-icons/Feather";

import styles from "./SearchBarStyles";

type SearchBarProps = {
query: string;
setQuery: (query: string) => void;
onFocus?: () => void;
onBlur?: () => void;
onClear: () => void;
onCancel: () => void;
isFocused: boolean;
inputRef: React.RefObject<TextInput>;
};

const SearchBar: React.FC<SearchBarProps> = ({
query,
setQuery,
onFocus,
onBlur,
onClear,
onCancel,
isFocused,
inputRef,
}) => {
return (
<View style={styles.searchContainer}>
<View style={styles.searchSection}>
<Icon name="search" size={20} color="gray" style={styles.searchIcon} />
<TextInput
ref={inputRef}
style={styles.input}
placeholder="Search"
value={query}
onChangeText={setQuery}
onFocus={onFocus}
onBlur={onBlur}
selectionColor="#909090"
/>
{query.length > 0 && (
<TouchableOpacity onPress={onClear} style={{ padding: 10 }}>
<Icon name="x" size={20} color="gray" />
</TouchableOpacity>
)}
</View>
{isFocused && (
<TouchableOpacity onPress={onCancel} style={styles.cancelButton}>
<Text>Cancel</Text>
</TouchableOpacity>
)}
</View>
);
};

export default SearchBar;
37 changes: 37 additions & 0 deletions dfm-sideline-sidekick-app/SearchBarStyles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { StyleSheet } from "react-native";

const styles = StyleSheet.create({
searchContainer: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
},
searchSection: {
flexDirection: "row",
flex: 1,
justifyContent: "center",
alignItems: "center",
borderWidth: 1,
borderColor: "rgba(0, 0, 0, 0.4)",
borderRadius: 10,
margin: 0,
marginBottom: 10,
},
input: {
flex: 1,
paddingVertical: 10,
color: "#424242",
},
searchIcon: {
padding: 10,
},
cancelButton: {
paddingLeft: 10,
marginBottom: 8,
justifyContent: "center",
alignItems: "center",
overflow: "visible",
},
});

export default styles;
2 changes: 1 addition & 1 deletion dfm-sideline-sidekick-app/pages/ConditionSectionStyles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default StyleSheet.create({
},
margin: {
marginLeft: 16,
marginTop: 20
marginTop: 20,
},
menu: {
display: "flex",
Expand Down
14 changes: 10 additions & 4 deletions dfm-sideline-sidekick-app/pages/ConditionsSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@ import { StackNavigationProp } from "@react-navigation/stack";
import * as Font from "expo-font";
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import React, { useEffect, useState } from "react";
import { Image, Pressable, SafeAreaView, ScrollView, Text, TouchableOpacity, View } from "react-native";
import {
Image,
Pressable,
SafeAreaView,
ScrollView,
Text,
TouchableOpacity,
View,
} from "react-native";

import StringRenderer from "../components/StringRenderer";

Expand All @@ -13,7 +21,6 @@ import styles from "./ConditionSectionStyles";

import type { Emergency } from "../emergencies";


export type RootStackParamList = {
// Define the parameters for your screens here
Conditions: { emergency: Emergency }; // Example parameter
Expand All @@ -32,7 +39,6 @@ type Props = {

type StringValue = string | string[] | { [key: string]: StringValue };

// eslint-disable-next-line @typescript-eslint/no-unused-vars
export default function ConditionsSection({ route, navigation }: Props) {
const [isOverviewPressed, setIsOverviewPressed] = useState<boolean>(true);
const [isTreatmentPressed, setIsTreatmentPressed] = useState<boolean>(false);
Expand Down Expand Up @@ -124,7 +130,7 @@ export default function ConditionsSection({ route, navigation }: Props) {
}}
>
{/* eslint-disable-next-line @typescript-eslint/no-unsafe-assignment */}
<Image style={styles.image} source={require("../assets/ic_caretleft.png")}/>
<Image style={styles.image} source={require("../assets/ic_caretleft.png")} />
</TouchableOpacity>
<View style={styles.margin}>
<Text style={styles.subtitle}>Medical Emergency</Text>
Expand Down
116 changes: 48 additions & 68 deletions dfm-sideline-sidekick-app/pages/SearchPage.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { useNavigation } from "@react-navigation/native";
import { StackNavigationProp } from "@react-navigation/stack";
import React, { useEffect, useRef, useState } from "react";
import React, { useRef, useState } from "react";
import { FlatList, Text, TextInput, TouchableOpacity, View } from "react-native";
import Icon from "react-native-vector-icons/Feather";

import { useData } from "../DataContext";
import { searchDocuments } from "../HandleSearch";
import SearchBar from "../SearchBarComponent";

import { RootStackParamList } from "./ConditionsSection";
import styles from "./GlobalSearchStyles";
Expand All @@ -21,56 +22,43 @@ type DocumentBase = {

type ConditionsNavigationProp = StackNavigationProp<RootStackParamList, "Conditions">;

const SearchBarComponent = () => {
const [query, setQuery] = useState("");
const SearchPage: React.FC = () => {
const [query, setQuery] = useState<string>("");
const [filteredDocuments, setFilteredDocuments] = useState<DocumentBase[]>([]);
const [isFocused, setIsFocused] = useState(false);
const inputRef = useRef<TextInput>(null);
const [isFocused, setIsFocused] = useState<boolean>(false);
const { jsonData } = useData();
const emergencies = jsonData?.emergencies ?? [];
const generalPrinciples = jsonData?.generalPrinciples ?? [];
const navigation = useNavigation<ConditionsNavigationProp>();
const inputRef = useRef<TextInput>(null);

const handleSearch = (text: string) => {
setQuery(text);
const allDocuments = [...emergencies, ...generalPrinciples];
const matchedDocuments = searchDocuments(allDocuments, text).map((doc) => ({
...doc,
_id: doc._id ?? "fallback-id",
subtitle:
doc.subtitle ??
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.", //default subtitle for now
}));
setFilteredDocuments(matchedDocuments);
};

const clearInput = () => {
setQuery("");
setFilteredDocuments([]);
};

const cancelSearch = () => {
setQuery("");
setFilteredDocuments([]);
setIsFocused(false);
if (inputRef.current !== null) {
inputRef.current.blur();
if (text) {
const allDocuments = [...emergencies, ...generalPrinciples];
const matchedDocuments = searchDocuments(allDocuments, text).map((doc) => ({
...doc,
_id: doc._id ?? "fallback-id",
subtitle:
doc.subtitle ??
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.",
}));
setFilteredDocuments(matchedDocuments);
} else {
setFilteredDocuments([]);
}
};

const highlightText = (text: string, input: string): React.ReactNode[] => {
// Split the input into individual words and escape special characters for regex
const words = input.split(/\s+/).map((word) => word.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&"));

// Create a regex pattern that matches any of the words
const pattern = words.join("|");
const queryRegex = new RegExp(`(${pattern})`, "gi");

const parts = text.split(queryRegex);

return parts.map((part, index) => {
const isMatch = queryRegex.test(part) && part.trim() !== "";
queryRegex.lastIndex = 0;

return (
<Text key={index.toString()} style={isMatch ? styles.highlightedText : undefined}>
{part}
Expand All @@ -79,46 +67,39 @@ const SearchBarComponent = () => {
});
};

useEffect(() => {
if (isFocused && inputRef.current) {
inputRef.current.focus();
const clearInput = () => {
const newQuery = "";
setQuery(newQuery);
handleSearch(newQuery);
setFilteredDocuments([]);
};

const cancelSearch = () => {
setQuery("");
setFilteredDocuments([]);
setIsFocused(false);
if (inputRef.current !== null) {
inputRef.current.blur();
}
}, [isFocused]);
};

return (
<View style={styles.container}>
<Text style={styles.title}>Global Search</Text>
<View style={styles.searchContainer}>
<View style={styles.searchSection}>
<Icon name="search" size={13} color="gray" style={styles.searchIcon} />
<TextInput
ref={inputRef}
style={styles.input}
placeholder="Search"
value={query}
onChangeText={handleSearch}
selectionColor="#909090"
onFocus={() => {
setIsFocused(true);
}}
onBlur={() => {
setIsFocused(false);
}}
/>
{query.length > 0 && (
<TouchableOpacity onPress={clearInput} style={{ padding: 10 }}>
<Icon name="x" size={15} color="gray" />
</TouchableOpacity>
)}
</View>
<View>
{isFocused && (
<TouchableOpacity onPress={cancelSearch} style={styles.cancelButton}>
<Text>Cancel</Text>
</TouchableOpacity>
)}
</View>
</View>
<SearchBar
query={query}
setQuery={handleSearch}
onFocus={() => {
setIsFocused(true);
}}
onBlur={() => {
setIsFocused(false);
}}
onClear={clearInput}
onCancel={cancelSearch}
isFocused={isFocused}
inputRef={inputRef}
/>
<View>
{isFocused ? (
query.length > 0 ? (
Expand Down Expand Up @@ -151,5 +132,4 @@ const SearchBarComponent = () => {
</View>
);
};

export default SearchBarComponent;
export default SearchPage;

0 comments on commit 2d13856

Please sign in to comment.