source: imaps-frontend/src/components/SearchBar/SearchBar.jsx@ 79a0317

main
Last change on this file since 79a0317 was 79a0317, checked in by stefan toskovski <stefantoska84@…>, 3 days ago

F4 Finalna Verzija

  • Property mode set to 100644
File size: 6.4 KB
Line 
1import React, { useState, useEffect, useRef } from "react";
2import ReactDOM from "react-dom";
3import searchIcon from "../../assets/search_icon.png";
4import routeIcon from "../../assets/route_icon.png";
5import closeIcon from "../../assets/close_icon.png";
6import styles from "./SearchBar.module.css";
7
8function SearchBar({
9 map,
10 handleDirectionsSubmit,
11 setIsPanelOpen,
12 setSelectedRoom,
13 availableShapes,
14 handleFloorChange,
15 }) {
16 const [isExpanded, setIsExpanded] = useState(false);
17 const [from, setFrom] = useState("");
18 const [to, setTo] = useState("");
19 const [availableOptions, setAvailableOptions] = useState([]);
20 const [filteredOptions, setFilteredOptions] = useState([]);
21 const [dropdownVisible, setDropdownVisible] = useState(false);
22 const [inputFieldType, setInputFieldType] = useState("");
23
24 const wrapperRef = useRef(null);
25 const dropdownContainerRef = useRef(null);
26 const activeInputRef = useRef(null); // Track the currently focused input field
27
28 const toggleExpanded = () => setIsExpanded(!isExpanded);
29
30 const handleClickOutside = (event) => {
31 if (
32 wrapperRef.current &&
33 !wrapperRef.current.contains(event.target) &&
34 dropdownContainerRef.current &&
35 !dropdownContainerRef.current.contains(event.target)
36 ) {
37 setDropdownVisible(false);
38 }
39 };
40
41 useEffect(() => {
42 document.addEventListener("mousedown", handleClickOutside);
43 return () => {
44 document.removeEventListener("mousedown", handleClickOutside);
45 };
46 }, []);
47
48 const searchRoom = () => {
49 const foundRoom = availableShapes.find((sh) => sh.info.name === from);
50 if (foundRoom && foundRoom.floorNum !== map.floorNum) {
51 handleFloorChange(foundRoom.floorNum);
52 }
53 map.highlightShape(from);
54 setSelectedRoom(foundRoom);
55 setIsPanelOpen(true);
56 };
57
58 const handleInputFocus = (field, inputRef) => {
59 if (availableOptions.length === 0 && map) {
60 setAvailableOptions(
61 availableShapes
62 .filter((sh) => sh.className === "RenderedRoom")
63 .map((shape) => shape.info.name)
64 );
65 }
66 setInputFieldType(field);
67 setDropdownVisible(true);
68 activeInputRef.current = inputRef; // Set the active input ref
69 };
70
71 const handleInputChange = (setter) => (event) => {
72 const value = event.target.value;
73 setter(value);
74 setDropdownVisible(true);
75
76 const filtered = availableOptions.filter((option) =>
77 option.toLowerCase().includes(value.toLowerCase())
78 );
79 setFilteredOptions(filtered);
80 };
81
82 const handleOptionClick = (option) => {
83 if (inputFieldType === "from") {
84 setFrom(option);
85 } else if (inputFieldType === "to") {
86 setTo(option);
87 }
88 setDropdownVisible(false);
89 };
90
91 const renderDropdown = () => {
92 if (!dropdownVisible || filteredOptions.length === 0) return null;
93
94 const position = activeInputRef.current?.getBoundingClientRect() || {
95 top: 0,
96 left: 0,
97 width: 0,
98 };
99
100 return ReactDOM.createPortal(
101 <ul
102 ref={dropdownContainerRef}
103 className={styles.dropdown}
104 style={{
105 position: "absolute",
106 top: position.top + position.height + window.scrollY,
107 left: position.left + window.scrollX,
108 width: position.width,
109 }}
110 >
111 {filteredOptions.map((option, index) => (
112 <li
113 key={index}
114 className={styles.dropdownItem}
115 onClick={() => handleOptionClick(option)}
116 >
117 {option}
118 </li>
119 ))}
120 </ul>,
121 document.body
122 );
123 };
124
125 return (
126 <div className={styles.wrapper} ref={wrapperRef}>
127 {!isExpanded ? (
128 <div className={styles.searchBar}>
129 <input
130 type="search"
131 className={styles.inputField}
132 placeholder="Search location"
133 aria-label="Search"
134 onFocus={(e) => handleInputFocus("from", e.target)}
135 onChange={handleInputChange(setFrom)}
136 value={from}
137 />
138 {renderDropdown()}
139 <div className={styles.buttons}>
140 <button
141 type="button"
142 className={styles.iconButton}
143 onClick={searchRoom}
144 >
145 <img src={searchIcon} alt="Search Icon" />
146 </button>
147 <button
148 type="button"
149 className={styles.iconButton}
150 onClick={toggleExpanded}
151 >
152 <img src={routeIcon} alt="Route Icon" />
153 </button>
154 </div>
155 </div>
156 ) : (
157 <div className={styles.directionsContainer}>
158 <div className={styles.directionsInputs}>
159 <input
160 type="text"
161 placeholder="From"
162 aria-label="From"
163 value={from}
164 onFocus={(e) => handleInputFocus("from", e.target)}
165 onChange={handleInputChange(setFrom)}
166 className={styles.inputField}
167 />
168 <input
169 type="text"
170 placeholder="To"
171 aria-label="To"
172 value={to}
173 onFocus={(e) => handleInputFocus("to", e.target)}
174 onChange={handleInputChange(setTo)}
175 className={styles.inputField}
176 />
177 {renderDropdown()}
178 </div>
179 <div className={styles.buttons}>
180 <button
181 type="button"
182 className={styles.iconButton}
183 onClick={() => handleDirectionsSubmit(from, to)}
184 >
185 <img src={searchIcon} alt="Submit Directions" />
186 </button>
187 <button
188 type="button"
189 className={styles.iconButton}
190 onClick={toggleExpanded}
191 >
192 <img src={closeIcon} alt="Close Icon" />
193 </button>
194 </div>
195 </div>
196 )}
197 </div>
198 );
199}
200
201export default SearchBar;
Note: See TracBrowser for help on using the repository browser.