Changeset 0c6b92a for imaps-frontend/src/components
- Timestamp:
- 12/12/24 17:06:06 (5 weeks ago)
- Branches:
- main
- Parents:
- d565449
- Location:
- imaps-frontend/src/components
- Files:
-
- 37 added
- 8 deleted
- 11 edited
- 4 moved
Legend:
- Unmodified
- Added
- Removed
-
imaps-frontend/src/components/FilterBar/FilterBar.jsx
rd565449 r0c6b92a 1 import React from "react";1 import React, { useState, useEffect, useCallback } from "react"; 2 2 import styles from "./FilterBar.module.css"; 3 3 4 function FilterBar() { 5 const filterLocation = (category) => { 6 console.log(`Filter locations by: ${category}`); 7 // filtering logic 8 }; 4 export default function FilterBar({roomTypes,map}) { 5 const [selectedCategory, setSelectedCategory] = useState("all"); 6 7 8 9 const filterLocation = useCallback((category) => { 10 map.setFilter(category) 11 setSelectedCategory(category); 12 },[]) 9 13 10 14 return ( 11 15 <div className={styles.wrapper}> 12 16 <div className={styles.scrollableContainer}> 13 <button className={styles.buttonValue} onClick={() => filterLocation("all")}> 17 <button 18 className={`${styles.buttonValue} ${selectedCategory === "all" ? styles.active : ""}`} 19 onClick={() => filterLocation("all")} 20 > 14 21 All 15 22 </button> 16 <button className={styles.buttonValue} onClick={() => filterLocation("Classrooms")}> 17 Classrooms 18 </button> 19 <button className={styles.buttonValue} onClick={() => filterLocation("Administrative")}> 20 Administrative 21 </button> 22 <button className={styles.buttonValue} onClick={() => filterLocation("Labs")}> 23 Labs 24 </button> 25 <button className={styles.buttonValue} onClick={() => filterLocation("Restrooms")}> 26 Restrooms 27 </button> 28 <button className={styles.buttonValue} onClick={() => filterLocation("Cafeteria")}> 29 Cafeteria 30 </button> 23 24 {roomTypes?.map((roomType, index) => ( 25 <button 26 key={index} 27 className={`${styles.buttonValue} ${selectedCategory === roomType.name ? styles.active : ""}`} 28 onClick={() => filterLocation(roomType.name)} 29 > 30 {roomType.name} 31 </button> 32 ))} 31 33 </div> 32 34 </div> … … 34 36 } 35 37 36 export default FilterBar; -
imaps-frontend/src/components/MapControls/MapControls.jsx
rd565449 r0c6b92a 1 import React, { useState} from "react";1 import React, {useEffect, useState} from "react"; 2 2 import styles from "./MapControls.module.css"; 3 3 import plusIcon from "../../assets/plus_icon.png"; 4 4 import minusIcon from "../../assets/minus_icon.png"; 5 5 import floorIcon from "../../assets/floor_icon.png"; 6 import HttpService from "../../scripts/net/HttpService.js"; 6 7 7 export default function MapControls({ onZoomIn, onZoomOut, onFloorChange }) { 8 const [currentFloor, setCurrentFloor] = useState(1); // Starting floor 8 export default function MapControls({onFloorChange,mapName }) { 9 const [currentFloor, setCurrentFloor] = useState(0); 10 const [floors, setFloors] = useState([]); 11 12 // Starting floor 9 13 10 14 // Handle floor selection … … 20 24 {/* Zoom Controls */} 21 25 <div className={styles.zoomControls}> 22 <button onClick={onZoomIn}className={styles.zoomButton}>26 <button className={styles.zoomButton}> 23 27 <img src={plusIcon} alt="Zoom In" /> 24 28 </button> 25 <button onClick={onZoomOut}className={styles.zoomButton}>29 <button className={styles.zoomButton}> 26 30 <img src={minusIcon} alt="Zoom Out" /> 27 31 </button> … … 29 33 30 34 {/* Floor Selector */} 31 <div className={styles.floorSelector}> 32 <img src={floorIcon} alt="Floor Icon" className={styles.floorIcon} /> 33 <select 34 value={currentFloor} 35 onChange={(e) => handleFloorChange(parseInt(e.target.value, 10))} 36 className={styles.floorDropdown} 37 > 38 <option value={1}>1F</option> 39 <option value={2}>2F</option> 40 <option value={3}>3F</option> 41 <option value={4}>4F</option> 42 </select> 43 </div> 35 {/*<div className={styles.floorSelector}>*/} 36 {/* <img src={floorIcon} alt="Floor Icon" className={styles.floorIcon} />*/} 37 {/* <select*/} 38 {/* value={currentFloor}*/} 39 {/* onChange={(e) => handleFloorChange(parseInt(e.target.value, 10))}*/} 40 {/* className={styles.floorDropdown}*/} 41 {/* >*/} 42 {/* {floors?.map(floor => (*/} 43 {/* <option key={floor.floorNumber} value={floor.floorNumber}>*/} 44 {/* {floor.floorNumber}F*/} 45 {/* </option>*/} 46 47 {/* ))}*/} 48 {/* /!*<option value={1}>1F</option>*!/*/} 49 {/* /!*<option value={2}>2F</option>*!/*/} 50 {/* /!*<option value={3}>3F</option>*!/*/} 51 {/* /!*<option value={4}>4F</option>*!/*/} 52 {/* </select>*/} 53 {/*</div>*/} 44 54 </div> 45 55 ); -
imaps-frontend/src/components/Modals/EntranceModal/EntranceModal.module.css
rd565449 r0c6b92a 14 14 } 15 15 16 /* Overlay: Dark background with optional blur */17 16 .overlay { 18 background: rgba(0, 0, 0, 0.7); /* Darken the background */ 19 backdrop-filter: blur(5px); /* Optional blur effect */ 20 z-index: 999; /* Ensure the overlay appears below the modal */ 17 /*background: rgba(255, 255, 255, 0.05);*/ 18 z-index: 999; 21 19 } 22 20 23 /* Modal content: Centered and styled */ 21 .draggableHeader { 22 cursor: move; 23 padding: 10px; 24 background-color: #2c2f33; 25 color: #ffffff; 26 border-bottom: 1px solid #444; 27 } 28 29 30 24 31 .modalContent { 25 32 position: absolute; … … 27 34 left: 50%; 28 35 transform: translate(-50%, -50%); 29 background-color: #2c2f33; /* Darker background for modal content */30 color: #ffffff ; /* White text for contrast */36 background-color: #2c2f33; 37 color: #ffffff !important; 31 38 padding: 20px; 32 39 border-radius: 8px; … … 34 41 width: 100%; 35 42 box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); 36 z-index: 1000; /* Ensure the modal appears above the overlay */43 z-index: 1000; 37 44 } 38 45 39 /* Heading styles */ 40 h2, 41 h3 { 46 47 .title { 42 48 margin-bottom: 15px; 43 color: #ffffff ; /* White text for the headings */49 color: #ffffff ; 44 50 } 45 51 46 /* Button for opening the modal */47 52 .btnModal { 48 53 padding: 10px 20px; … … 61 66 } 62 67 63 /* Close button inside the modal */64 68 .closeModal { 65 69 position: absolute; … … 78 82 } 79 83 80 /* Form styles */81 84 .form { 82 85 display: flex; … … 88 91 } 89 92 90 /* Form labels */91 93 .formGroup label { 92 94 display: block; 93 95 margin-bottom: 5px; 94 color: #ffffff; /* White text for labels */96 color: #ffffff; 95 97 } 96 98 97 /* Input, select, and textarea styles */98 99 .formGroup input, 99 100 .formGroup select, … … 103 104 border-radius: 4px; 104 105 border: 1px solid #ccc; 105 background-color: #23272a; /* Darker input background */106 color: #ffffff; /* White text inside the input fields */106 background-color: #23272a; 107 color: #ffffff; 107 108 } 108 109 109 110 .formGroup input::placeholder, 110 111 .formGroup textarea::placeholder { 111 color: #888888; /* Slightly lighter color for placeholder text */112 color: #888888; 112 113 } 113 114 114 115 /* Checkbox alignment fix */ 115 116 .formGroupCheckbox { 117 116 118 display: flex; 117 align-items: center; /* Vertical alignment */ 118 color: #ffffff; 119 flex-direction: row; 120 vertical-align: middle; 121 align-items: flex-end; 122 /*justify-content: start;!* Vertical alignment *!*/ 123 /*color: #ffffff;*/ 124 } 125 126 .formGroupCheckbox input{ 127 width: 10%; 128 margin-bottom: 0.88em; 119 129 } 120 130 121 131 /* Add button */ 122 132 .addButton { 123 background-color: #28a745 ;133 background-color: #28a745 !important; 124 134 color: white; 125 135 padding: 8px 15px; … … 131 141 132 142 .addButton:hover { 133 background-color: #218838 ;143 background-color: #218838 !important; 134 144 } 135 145 136 146 /* Save button styling */ 137 147 .submitButton { 138 background-color: #28a745 ;148 background-color: #28a745 !important; 139 149 color: white; 140 150 padding: 10px 20px; … … 150 160 } 151 161 152 /* Pin list */153 162 .pinList { 154 163 list-style-type: none; … … 157 166 } 158 167 159 /* Individual pin item */160 168 .pinItem { 161 169 background-color: #444; … … 169 177 } 170 178 171 /* Remove button */172 179 .removeButton { 173 180 background-color: #dc3545; -
imaps-frontend/src/components/Profile/Profile.jsx
rd565449 r0c6b92a 1 import React, { useState, useRef, useEffect } from "react"; 1 import React, { useState, useRef, useEffect, useContext } from "react"; 2 import { useNavigate, Link } from "react-router-dom"; 2 3 import profile from "../../assets/person_icon.png"; 3 4 import styles from "./Profile.module.css"; 5 import { useAppContext } from "../AppContext/AppContext.jsx"; 4 6 5 function Profile() { 6 const menus = ["Profile", "Settings", "Support", "Logout"]; 7 const [open, setOpen] = useState(false); 8 const menuRef = useRef(null); 9 const imgRef = useRef(null); 7 function Profile({ position = "fixed" }) { 8 const { username, isAuthenticated } = useAppContext(); 9 const [open, setOpen] = useState(false); 10 const menuRef = useRef(null); 11 const imgRef = useRef(null); 12 const navigate = useNavigate(); 10 13 11 useEffect(() => { 12 const handleClickOutside = (e) => { 13 if (menuRef.current && imgRef.current) { 14 if (!menuRef.current.contains(e.target) && !imgRef.current.contains(e.target)) { 15 setOpen(false); 14 const menus = isAuthenticated ? ["My Maps", "Logout"] : ["Login"]; 15 16 useEffect(() => { 17 const handleClickOutside = (e) => { 18 if (menuRef.current && imgRef.current) { 19 if (!menuRef.current.contains(e.target) && !imgRef.current.contains(e.target)) { 20 setOpen(false); 21 } 22 } 23 }; 24 25 document.addEventListener("click", handleClickOutside); 26 27 return () => { 28 document.removeEventListener("click", handleClickOutside); 29 }; 30 }, []); 31 32 const handleMenuClick = (menu) => { 33 if (menu === "My Maps") { 34 navigate("/MyMaps"); 35 } else if (menu === "Logout") { 36 localStorage.removeItem("token"); 37 window.location.reload(); 16 38 } 17 }39 setOpen(false); 18 40 }; 19 41 20 document.addEventListener("click", handleClickOutside); 21 22 return () => { 23 document.removeEventListener("click", handleClickOutside); 24 }; 25 }, []); 26 27 return ( 28 <div className={styles.profileContainer}> 29 <div className={styles.profileWrapper}> 30 <img 31 onClick={() => setOpen(!open)} 32 src={profile} 33 alt="profile" 34 className={styles.profileImage} 35 ref={imgRef} 36 /> 37 {open && ( 38 <div ref={menuRef} className={styles.dropdownMenu}> 39 <ul className={styles.menuList}> 40 {menus.map((menu) => ( 41 <li key={menu} onClick={() => setOpen(false)} className={styles.menuItem}> 42 {menu} 43 </li> 44 ))} 45 </ul> 46 </div> 47 )} 48 </div> 49 </div> 50 ); 42 return ( 43 <div className={position === "fixed" ? styles.fixedProfileContainer : styles.inlineProfileContainer}> 44 <div className={styles.profileWrapper}> 45 <div className={styles.profileIconContainer} onClick={() => setOpen(!open)}> 46 <img src={profile} alt="profile" className={styles.profileImage} ref={imgRef} /> 47 </div> 48 {open && ( 49 <div ref={menuRef} className={styles.dropdownMenu}> 50 {isAuthenticated && <div className={styles.username}>{username}</div>} 51 <ul className={styles.menuList}> 52 {menus.map((menu) => 53 menu === "Login" ? ( 54 <li key={menu} className={styles.menuItem}> 55 <Link to="/login" className={styles.linkStyle}>{menu}</Link> 56 </li> 57 ) : ( 58 <li key={menu} onClick={() => handleMenuClick(menu)} className={styles.menuItem}> 59 {menu} 60 </li> 61 ) 62 )} 63 </ul> 64 </div> 65 )} 66 </div> 67 </div> 68 ); 51 69 } 52 70 -
imaps-frontend/src/components/Profile/Profile.module.css
rd565449 r0c6b92a 1 img:hover, 2 .menuItem:hover { 1 .fixedProfileContainer { 2 position: fixed; 3 top: 1em; 4 right: 2.5em; 5 z-index: 1000; 6 } 7 8 .inlineProfileContainer { 9 display: inline-block; 3 10 cursor: pointer; 4 11 } 5 12 6 .menuList { 7 list-style: none; 8 padding: 0; 9 margin: 0; 10 text-align: center; 13 .profileWrapper { 14 display: flex; 15 align-items: center; 16 cursor: pointer; 11 17 } 12 18 13 .profileContainer { 14 display: flex; 15 justify-content: center; 16 } 17 18 .profileWrapper { 19 position: relative; 19 .profileIconContainer { 20 position: relative; /* Makes dropdown position relative to this container */ 21 cursor: pointer; 20 22 } 21 23 22 24 .profileImage { 23 height: 48px;24 width: 48px;25 width: 60px; 26 height: 60px; 25 27 border-radius: 50%; 26 /* border: 4px solid #d1d5db; */ 27 object-fit: cover; 28 transition: box-shadow 0.3s ease; 28 29 } 29 30 30 /* Dropdown Menu */ 31 .profileImage:hover { 32 box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.2); 33 } 34 31 35 .dropdownMenu { 32 36 position: absolute; 33 top: 50px; /* Adjust according to image size and spacing */ 34 left: 50%; 35 transform: translateX(-50%); /* Center the menu below the image */ 36 width: 13rem; 37 background-color: white; 38 padding: 1rem; 39 box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); 37 top: 70px; 38 margin-left: 1.8em; 39 transform: translateX(-50%); 40 background-color: #ffffff; 41 box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15); 42 border-radius: 12px; 43 padding: 12px; 44 width: 180px; 45 transition: opacity 0.3s ease; 46 opacity: 1; 40 47 } 41 48 42 /* Menu List and Items */ 49 .username { 50 padding: 10px 0; 51 font-weight: 600; 52 text-align: center; 53 color: #333; 54 border-bottom: 1px solid #eee; 55 } 56 43 57 .menuList { 44 58 list-style-type: none; … … 49 63 .menuItem { 50 64 padding: 10px; 65 cursor: pointer; 66 transition: background-color 0.2s ease, color 0.2s ease; 51 67 text-align: center; 52 border-radius: 5px; 53 transition: background-color 0.3s ease; 68 color: #333; 69 font-weight: 500; 70 border-radius: 8px; 54 71 } 55 72 56 73 .menuItem:hover { 57 background-color: #f0f0f0; 74 background-color: #f5f5f5; 75 color: #007bff; /* Optional: Adds a blue highlight on hover */ 58 76 } -
imaps-frontend/src/components/ProtectedRoute/ProtectedRoute.jsx
rd565449 r0c6b92a 1 import React from "react"; 2 import { Outlet, Navigate } from "react-router-dom"; 1 import { Navigate, Outlet, useLocation } from "react-router-dom"; 2 import {useAppContext} from "../AppContext/AppContext.jsx"; 3 import HttpService from "../../scripts/net/HttpService.js"; 4 import React, {useEffect} from "react"; 5 import {LoadingContainer} from "../LoadingContainer/LoadingContainer.jsx"; 6 import config from "../../scripts/net/netconfig.js"; 7 import {verifyToken} from "../../scripts/util/verifyToken.js"; 3 8 4 const ProtectedRoute = ({ isAuthenticated }) => { 5 return isAuthenticated ? <Outlet /> : <Navigate to="/Login" />; 9 const ProtectedRoute = () => { 10 const location = useLocation(); 11 const {loading,isAuthenticated} = useAppContext(); 12 13 14 if(loading){ 15 return <LoadingContainer/> 16 } 17 18 19 if (!isAuthenticated) { 20 return <Navigate to="/Login" state={{ targetPath: location }} replace />; 21 } 22 23 return <Outlet />; 6 24 }; 7 25 -
imaps-frontend/src/components/SaveMap/SaveMap.jsx
rd565449 r0c6b92a 2 2 import HttpService from '../../scripts/net/HttpService'; 3 3 import styles from './SaveMap.module.css'; 4 import triggerMapSave from "../../scripts/util/triggerMapSave.js"; 4 5 5 6 const SaveMap = ({submitHandler}) => { … … 8 9 const handleSubmit = (event) => { 9 10 event.preventDefault(); 10 submitHandler(name); 11 // submitHandler(); 12 // const mapSaveEvent = new CustomEvent("mapsave",{}); 13 // window.dispatchEvent(mapSaveEvent) 14 triggerMapSave(); 11 15 }; 12 16 … … 15 19 <div className={styles.saveMapContainer}> 16 20 <form onSubmit={handleSubmit} className={styles.saveMapForm}> 17 <div>18 <label htmlFor="name">Map Name: </label>19 <input20 type="text"21 id="name"22 value={name}23 onChange={(e) => setName(e.target.value)}24 placeholder="Enter map name"25 required26 className={styles.saveMapInput}27 />28 </div>29 21 <div> 30 22 <button type="submit" className={styles.saveMapButton}>Save Map</button> -
imaps-frontend/src/components/SaveMap/SaveMap.module.css
rd565449 r0c6b92a 21 21 22 22 .saveMapButton { 23 background-color: #e67e22; 24 color: white; 23 width: 100%; 24 height: 50px; 25 background-color: #e67e22 !important; 26 color: #ffffff !important; 25 27 border: none; 26 border-radius: 5px; 28 border-radius: 8px; 29 font-size: 20px; 30 font-weight: bold; 27 31 cursor: pointer; 28 width: 8vw; 29 height: 2vw; 30 text-align: center; 32 display: flex; 33 justify-content: center; 34 align-items: center; 35 box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); 36 transition: background-color 0.3s, transform 0.2s, box-shadow 0.3s; 37 appearance: none; 38 -webkit-appearance: none; 31 39 } 32 40 33 41 .saveMapButton:hover { 34 background-color: #b35418; 42 background-color: #d2691e !important; 43 transform: scale(1.05); 44 box-shadow: 0 6px 8px rgba(0, 0, 0, 0.15); 45 } 46 47 .saveMapButton:active { 48 background-color: #b35418 !important; 35 49 transform: scale(0.98); 36 50 box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); -
imaps-frontend/src/components/SearchBar/SearchBar.jsx
rd565449 r0c6b92a 1 import React, { useState } from "react"; 1 import React, { useState, useEffect, useRef } from "react"; 2 import ReactDOM from "react-dom"; 2 3 import searchIcon from "../../assets/search_icon.png"; 3 4 import routeIcon from "../../assets/route_icon.png"; … … 5 6 import styles from "./SearchBar.module.css"; 6 7 7 function SearchBar( props) {8 function SearchBar({ map, handleDirectionsSubmit, setIsPanelOpen, setSelectedRoom, availableShapes,handleFloorChange }) { 8 9 const [isExpanded, setIsExpanded] = useState(false); 9 10 const [from, setFrom] = useState(""); 10 11 const [to, setTo] = useState(""); 12 const [availableOptions, setAvailableOptions] = useState([]); 13 const [filteredOptions, setFilteredOptions] = useState([]); 14 const [dropdownVisible, setDropdownVisible] = useState(false); 15 const [inputFieldType, setInputFieldType] = useState(""); 16 const dropdownRef = useRef(null); 11 17 12 18 const toggleExpanded = () => { … … 14 20 }; 15 21 16 function searchRoom(){ 17 props.map.search(); 22 function searchRoom() { 23 let foundRoom = availableShapes.find(sh => sh.info.name === from) 24 console.log("map fnum",map.floorNum) 25 if(foundRoom.floorNum !== map.floorNum){ 26 handleFloorChange(foundRoom.floorNum); 27 } 18 28 29 30 console.log("FOUND ROOM: " + foundRoom) 31 map.highlightShape(from); 32 setSelectedRoom(foundRoom); 33 setIsPanelOpen(true); 19 34 } 20 35 21 const handleDirectionsSubmit = () => { 22 console.log(`From: ${from}, To: ${to}`); 23 const url = new URL('http://localhost:8080/api/public/navigate'); 24 url.searchParams.append('from', from); 25 url.searchParams.append('to', to); 36 const handleInputFocus = (field) => { 37 if (availableOptions.length === 0 && map) { 38 setAvailableOptions( 39 availableShapes 40 .filter((sh) => sh.className === "RenderedRoom") 41 .map((shape) => shape.info.name) 42 ); 43 } 44 setDropdownVisible(true); 45 setInputFieldType(field); 46 }; 26 47 27 fetch(url) 28 .then(response => { 29 if (!response.ok) { 30 throw new Error('Network response was not ok'); 31 } 32 return response.json(); 33 }) 34 .then(data => { 35 console.log('Success:', data); 36 const points = data.map(item => item.coordinates); 37 props.map.drawRoute(points); 38 }) 39 .catch(error => { 40 console.error('Error:', error); 41 }); 48 const handleInputChange = (setter) => (event) => { 49 const value = event.target.value; 50 setter(value); 51 setDropdownVisible(true); 52 53 const filtered = availableOptions.filter((option) => 54 option.toLowerCase().includes(value.toLowerCase()) 55 ); 56 setFilteredOptions(filtered); 57 }; 58 59 const handleOptionClick = (option) => { 60 if (inputFieldType === "from") { 61 setFrom(option); 62 } else if (inputFieldType === "to") { 63 setTo(option); 64 } 65 setDropdownVisible(false); 66 }; 67 68 const renderDropdown = () => { 69 if (!dropdownVisible || filteredOptions.length === 0) return null; 70 71 const position = dropdownRef.current?.getBoundingClientRect() || { top: 0, left: 0, width: 0 }; 72 73 return ReactDOM.createPortal( 74 <ul 75 className={styles.dropdown} 76 style={{ 77 position: "absolute", 78 top: position.top + position.height, 79 left: position.left, 80 width: position.width, 81 }} 82 > 83 {filteredOptions.map((option, index) => ( 84 <li 85 key={index} 86 className={styles.dropdownItem} 87 onClick={() => handleOptionClick(option)} 88 > 89 {option} 90 </li> 91 ))} 92 </ul>, 93 document.body // Portal renders outside the parent hierarchy 94 ); 42 95 }; 43 96 44 97 return ( 45 <div className={styles.wrapper}> 46 {/* Regular search bar */} 47 {!isExpanded ? ( 48 <div className={styles.searchBar}> 49 <input 50 type="search" 51 className={styles.inputField} 52 placeholder="Search location" 53 aria-label="Search" 54 /> 55 <div className={styles.buttons}> 56 <button type="button" className={styles.iconButton} onClick={searchRoom}> 57 <img src={searchIcon} alt="Search Icon" /> 58 </button> 59 <button type="button" className={styles.iconButton} onClick={toggleExpanded}> 60 <img src={routeIcon} alt="Route Icon" /> 61 </button> 62 </div> 63 </div> 64 ) : ( 65 /* Expanded view for directions */ 66 <div className={styles.directionsContainer}> 67 <div className={styles.directionsInputs}> 68 <input 69 type="text" 70 placeholder="From" 71 aria-label="From" 72 value={from} 73 onChange={(e) => setFrom(e.target.value)} 74 className={styles.inputField} 75 /> 76 <input 77 type="text" 78 placeholder="To" 79 aria-label="To" 80 value={to} 81 onChange={(e) => setTo(e.target.value)} 82 className={styles.inputField} 83 /> 84 </div> 85 <div className={styles.buttons}> 86 <button type="button" className={styles.iconButton} onClick={handleDirectionsSubmit}> 87 <img src={searchIcon} alt="Submit Directions" /> 88 </button> 89 <button type="button" className={styles.iconButton} onClick={toggleExpanded}> 90 <img src={closeIcon} alt="Close Icon" /> 91 </button> 92 </div> 93 </div> 94 )} 95 </div> 98 <div className={styles.wrapper}> 99 {!isExpanded ? ( 100 <div className={styles.searchBar}> 101 <input 102 type="search" 103 className={styles.inputField} 104 placeholder="Search location" 105 aria-label="Search" 106 ref={dropdownRef} // Attach the input to calculate dropdown position 107 onFocus={() => handleInputFocus("from")} 108 onChange={handleInputChange(setFrom)} 109 value={from} 110 /> 111 {renderDropdown()} 112 <div className={styles.buttons}> 113 <button type="button" className={styles.iconButton} onClick={searchRoom}> 114 <img src={searchIcon} alt="Search Icon" /> 115 </button> 116 <button type="button" className={styles.iconButton} onClick={toggleExpanded}> 117 <img src={routeIcon} alt="Route Icon" /> 118 </button> 119 </div> 120 </div> 121 ) : ( 122 <div className={styles.directionsContainer}> 123 <div className={styles.directionsInputs}> 124 <input 125 type="text" 126 placeholder="From" 127 aria-label="From" 128 value={from} 129 onFocus={() => handleInputFocus("from")} 130 onChange={handleInputChange(setFrom)} 131 className={styles.inputField} 132 ref={inputFieldType === "from" ? dropdownRef : null} 133 /> 134 <input 135 type="text" 136 placeholder="To" 137 aria-label="To" 138 value={to} 139 onFocus={() => handleInputFocus("to")} 140 onChange={handleInputChange(setTo)} 141 className={styles.inputField} 142 ref={inputFieldType === "to" ? dropdownRef : null} 143 /> 144 {renderDropdown()} 145 </div> 146 <div className={styles.buttons}> 147 <button 148 type="button" 149 className={styles.iconButton} 150 onClick={() => handleDirectionsSubmit(from, to)} 151 > 152 <img src={searchIcon} alt="Submit Directions" /> 153 </button> 154 <button type="button" className={styles.iconButton} onClick={toggleExpanded}> 155 <img src={closeIcon} alt="Close Icon" /> 156 </button> 157 </div> 158 </div> 159 )} 160 </div> 96 161 ); 97 162 } -
imaps-frontend/src/components/SearchBar/SearchBar.module.css
rd565449 r0c6b92a 1 1 .wrapper { 2 position: relative; 2 3 display: flex; 3 4 justify-content: center; 4 margin: 0px auto; 5 margin: 0 auto; 6 z-index: 105; 5 7 max-width: 600px; 6 8 width: 100%; 9 padding: 10px; 7 10 } 8 11 9 12 .searchBar, 10 13 .directionsContainer { 14 position: relative; 11 15 display: flex; 12 align-items: end; /* Ensure all items remain aligned in the center */13 width: 100%;16 align-items: center; 17 /*width: 100%;*/ 14 18 background-color: white; 15 19 padding: 10px; … … 17 21 border-radius: 30px; 18 22 box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); 19 flex-wrap: nowrap; /* Prevent items from wrapping to the next line */23 flex-wrap: nowrap; 20 24 } 21 25 … … 28 32 outline: none; 29 33 margin-right: 10px; 30 min-width: 0; /* Ensure the input field remains flexible */ 34 min-width: 0; 35 31 36 } 37 38 .dropdown { 39 position: absolute; /* Position absolute to detach from wrapper flow */ 40 top: 100%; /* Place below the search bar */ 41 left: 0; 42 background-color: white; 43 border: 1px solid #ddd; 44 border-radius: 5px; 45 width: 100%; /* Match the search bar width */ 46 max-height: 200px; 47 overflow-y: auto; 48 z-index: 9999; /* Highest level to ensure it's above everything */ 49 box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); 50 } 51 52 53 .dropdownItem { 54 padding: 10px; 55 cursor: pointer; 56 transition: background-color 0.3s ease, color 0.3s ease; 57 } 58 59 .dropdownItem:hover { 60 background-color: #007bff; /* Highlight color */ 61 color: white; /* Ensure text is readable */ 62 } 63 32 64 33 65 .buttons { … … 50 82 font-size: 16px; 51 83 transition: background-color 0.3s ease; 52 width: auto;53 84 } 54 85 … … 62 93 } 63 94 64 /* Expanded view for the direction inputs */65 95 .directionsContainer { 66 flex-direction: column;67 96 gap: 10px; 68 97 } … … 73 102 gap: 10px; 74 103 } 75 76 .directionsInputs .inputField {77 flex: 1;78 }79 80 /* Media query for responsive design */81 @media (max-width: 768px) {82 .wrapper {83 max-width: 100%;84 padding: 10px;85 }86 87 /* Keep buttons and input fields inline */88 .searchBar,89 .directionsContainer {90 flex-direction: row; /* Keep items in a row */91 align-items: center; /* Keep buttons and inputs on the same line */92 flex-wrap: nowrap; /* Prevent items from wrapping */93 padding: 8px;94 }95 96 .inputField {97 width: 100%;98 flex-grow: 1;99 margin-right: 10px;100 }101 102 .buttons {103 width: auto;104 gap: 5px;105 }106 107 .iconButton {108 width: auto;109 padding: 10px;110 }111 } -
imaps-frontend/src/components/SideBar/SideBar.jsx
rd565449 r0c6b92a 24 24 </li> 25 25 <li> 26 <Link to="/Maps"> Maps</Link>26 <Link to="/Maps">Browse Maps</Link> 27 27 </li> 28 28 <li> 29 <Link to="/ Maps/FinkiMaps/Draw">Draw</Link>29 <Link to="/myMaps">My Maps</Link> 30 30 </li> 31 31 </ul> -
imaps-frontend/src/components/SideBar/SideBar.module.css
rd565449 r0c6b92a 90 90 top: 0; 91 91 left: 0; 92 width: 100 %;93 height: 100 %;92 width: 100vw; 93 height: 100vh; 94 94 background-color: rgba(0, 0, 0, 0.4); 95 z-index: 900;95 z-index: 100; 96 96 }
Note:
See TracChangeset
for help on using the changeset viewer.