Skip to content

Commit

Permalink
Feature/searcbox/countries statistic (reactplay#526)
Browse files Browse the repository at this point in the history
* completed search suggestion UI

* search suggestion

* search functionality

* added the auto search functionality

* searchbox functionality completed

* on map click update the searchbox

* added currency symbol along with name

* currency sign, population with commas, disabled autocomeplete

* clickable searcbar functionality

* tab index of map disabled

* sorted the search suggestion, defocus event,fixed the unique key warning,

* tabindex for input and searchbtn, searchOnKeyEnter function

* fixed the scrolling on key press

* bug fix hide search results onClikc searchbtn

* fixed scrolling bugs

Co-authored-by: Koustov <[email protected]>
  • Loading branch information
Deepak8717 and koustov committed Sep 22, 2022
1 parent 888e843 commit 31d391e
Show file tree
Hide file tree
Showing 6 changed files with 252 additions and 17 deletions.
3 changes: 3 additions & 0 deletions src/plays/countries-statics/Context.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { createContext } from "react";

export const GeoContext = createContext();
139 changes: 133 additions & 6 deletions src/plays/countries-statics/CountriesStatics.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,117 @@
import PlayHeader from "common/playlists/PlayHeader";
import Map from "./Map";
import Country from "./Country";
import { useState } from "react";
import { useEffect, useRef, useState } from "react";
import SearchAndFilter from "./SearchAndFilter";
import { GeoContext } from "./Context";
import MapData from "./featues.json";

function CountriesStatics(props) {
// Your Code Start below.
const [activeGeo, setActiveGeo] = useState("ind");
const [showSuggestions, setShowSuggestions] = useState(false);
const [index, setIndex] = useState(0);
const [selected, setSelected] = useState("");
const [searchQuery, setSearchQuery] = useState("india");
const searchSuggestionRef = useRef(null);
const searchResultItemRef = useRef();

//search query return the first 10 matching results.
const SearchResult = MapData.objects.world.geometries.filter((o) =>
o.properties.name.toLowerCase().includes(searchQuery.toLowerCase().trim())
);
SearchResult.sort((a, b) =>
a.properties.name.localeCompare(b.properties.name)
);

const keyPressHandler = (e) => {
const length = SearchResult.length;
//downArrow Key

if (e.keyCode === 40) {
if (index < length - 1) {
searchSuggestionRef.current.focus();
if (e.target.value === "") {
setIndex(0);
searchSuggestionRef.current.scrollTo(0, 0);
} else {
setIndex(index + 1);
}
} else {
setIndex(0);
}
}
//upArrow Key
if (e.keyCode === 38) {
if (index > 0) {
searchSuggestionRef.current.focus();
if (e.target.value === "") {
setIndex(0);
searchSuggestionRef.current.scrollTo(0, 0);
} else setIndex(index - 1);
}
}
//esc key
if (e.keyCode === 27) {
setShowSuggestions(false);
e.currentTarget.blur();
}
//Enter key
if (e.keyCode === 13) {
MapData.objects.world.geometries.map((country) => {
if (country.properties.name.toLowerCase() === selected.toLowerCase()) {
setActiveGeo(country.id);
setShowSuggestions(false);
}
});
setSelected(SearchResult[index].properties.name);
setActiveGeo(SearchResult[index].id);
}
};

useEffect(() => {
setSelected(SearchResult[index].properties.name);
searchResultItemRef.current &&
searchResultItemRef.current.scrollIntoView({
behavior: "smooth",
block: "center",
});
}, [index, showSuggestions]);

const searchInputClickHandler = () => {
setShowSuggestions(true);
};
const handleOnchange = (e) => {
setShowSuggestions(true);
setSearchQuery(e.target.value);
setSelected(e.target.value);
};
const handleOnFocus = () => {
setShowSuggestions(true);
};
const handleOnBlur = (e) => {
if (!e.currentTarget.contains(e.relatedTarget)) {
setShowSuggestions(false);
}
};
const searchResultClickHandler = (i) => {
setIndex(i);
setSelected(SearchResult[i].properties.name);
setShowSuggestions(false);
setActiveGeo(SearchResult[i].id);
};
const searchbarClickHandler = () => {
MapData.objects.world.geometries.map((country) => {
if (country.properties.name.toLowerCase() === selected.toLowerCase()) {
setActiveGeo(country.id);
setShowSuggestions(false);
console.log(showSuggestions);
}
});
};

const handleClickMap = (geo) => {
setSelected(geo.properties.name);
setActiveGeo(geo.id.toLowerCase());
};
return (
Expand All @@ -16,15 +121,37 @@ function CountriesStatics(props) {
<div className="play-details-body">
{/* Your Code Starts Here */}
<h1 className="text-4xl mt-4 font-bold text-center capitalize">
Geo Now
Geography Now
</h1>
<h2 className="text-xl text-center capitalize">
Learn geography in fun and interactive way
</h2>
<div className="flex flex-col xl:flex-row justify-between">
<Map activeGeo={activeGeo} handleClickMap={handleClickMap} />
<Country activeGeo={activeGeo} />
</div>
<GeoContext.Provider
value={{
activeGeo,
showSuggestions,
index,
selected,
searchQuery,
SearchResult,
searchSuggestionRef,
searchResultItemRef,
handleOnBlur,
keyPressHandler,
handleOnchange,
handleOnFocus,
searchResultClickHandler,
handleClickMap,
searchbarClickHandler,
searchInputClickHandler,
}}
>
<SearchAndFilter />
<div className="flex flex-col xl:flex-row justify-between">
<Map />
<Country activeGeo={activeGeo} />
</div>
</GeoContext.Provider>

{/* Your Code Ends Here */}
</div>
Expand Down
20 changes: 12 additions & 8 deletions src/plays/countries-statics/Country.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export default function Country({ activeGeo }) {
src={country.flags.png}
className="w-[350px] h-[200px] my-2 mx-auto md:mt-6"
/>
<div className="w-[350px] md:w-[500px] xl:w-[350px] mx-auto p-4">
<div className="w-[400px] md:w-[500px] xl:w-[400px] mx-auto pl-4 ">
<div className="flex m-1">
<div className="w-[120px] text-xl font-semibold capitalize ">
Capital
Expand All @@ -68,12 +68,16 @@ export default function Country({ activeGeo }) {
{Object.keys(country.currencies).map(
(currency, index) => {
return (
<span
key={index}
className="bg-lime-600 text-white px-1 rounded m-1"
>
{currency}
</span>
<div key={index}>
<span
key={index}
className=" text-2xl px-1 rounded m-1"
>
{country.currencies[currency].symbol},
</span>
<span>{currency}</span>,
<div>{country.currencies[currency].name}</div>
</div>
);
}
)}
Expand All @@ -84,7 +88,7 @@ export default function Country({ activeGeo }) {
Population
</div>
<div className="text-xl w-[200px] ">
{country.population}
{country.population.toLocaleString()}
</div>
</div>
<div className="flex m-1">
Expand Down
12 changes: 9 additions & 3 deletions src/plays/countries-statics/Map.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { useContext } from "react";
import { ComposableMap, Geographies, Geography } from "react-simple-maps";
import mapdata from "./featues.json";
export default function Map({ activeGeo, handleClickMap }) {
import { GeoContext } from "./Context";
export default function Map() {
const { activeGeo, handleClickMap } = useContext(GeoContext);

const activeStyle = {
default: { outline: "none" },
hover: { outline: "none" },
Expand All @@ -11,19 +15,21 @@ export default function Map({ activeGeo, handleClickMap }) {
hover: { outline: "none", fill: "#C0C0C0" },
pressed: { outline: "none" },
};

return (
<>
<div className="w-full xl:w-[60%] ">
<ComposableMap width={800} height={600}>
<ComposableMap width={800} height={550}>
<Geographies geography={mapdata}>
{({ geographies }) =>
geographies.map((geo) => {
return (
<Geography
tabIndex="-1"
key={geo.rsmKey}
geography={geo}
fill={
activeGeo === geo.id.toLowerCase()
activeGeo.toLowerCase() === geo.id.toLowerCase()
? "darkblue"
: "lightblue"
}
Expand Down
79 changes: 79 additions & 0 deletions src/plays/countries-statics/SearchAndFilter.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import React, { useContext } from "react";
import { BsSearch } from "react-icons/bs";
import "./countriesStatistic.css";

import { GeoContext } from "./Context";

const SearchAndFilter = () => {
const {
showSuggestions,
index,
selected,
SearchResult,
searchSuggestionRef,
searchResultItemRef,
keyPressHandler,
handleOnchange,
handleOnFocus,
handleOnBlur,
searchResultClickHandler,
searchbarClickHandler,
searchInputClickHandler,
} = useContext(GeoContext);

return (
<div className="w-full max-w-[550px] mx-auto flex justify-center">
<div
className="relative w-full h-fit"
tabIndex="0"
onBlur={(e) => handleOnBlur(e)}
>
<input
tabIndex="0"
type="text"
className="geo-now-searchbar w-full rounded-lg bg-inherit my-4"
placeholder="Enter the country..."
value={selected}
onChange={(e) => handleOnchange(e)}
onFocus={handleOnFocus}
autoComplete="new-password"
onClick={searchInputClickHandler}
onKeyDown={keyPressHandler}
/>
<div
tabIndex="0"
className="absolute top-[50%] translate-y-[-50%] right-0 w-[30px] h-[30px] mx-2 bg-cyan-500 cursor-pointer"
onClick={searchbarClickHandler}
onKeyDown={keyPressHandler}
>
<BsSearch className="absolute top-1 left-1 text-2xl text-white " />
</div>
{showSuggestions && (
<div
tabIndex="-1"
ref={searchSuggestionRef}
className="absolute w-full max-h-[290px] bg-white p-4 overflow-y-auto outline-none"
onKeyDown={(e) => keyPressHandler(e)}
>
{SearchResult.map((country, i) => {
return (
<div
key={i}
ref={index === i ? searchResultItemRef : null}
onClick={() => searchResultClickHandler(i)}
className={`border-b cursor-pointer hover:bg-cyan-100 font-bold px-2 my-[0.9rem] ${
index === i ? "geo-search-selected" : ""
}`}
>
{country.properties.name}
</div>
);
})}
</div>
)}
</div>
</div>
);
};

export default SearchAndFilter;
16 changes: 16 additions & 0 deletions src/plays/countries-statics/countriesStatistic.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.geo-now-searchbar {
border: 2px solid lightblue !important;
outline: none;
padding-left: 1.5rem !important;
}
.geo-now-searchbar:focus {
outline: solid;
outline-color: lightblue;
outline-width: 2px;
border: none;
background-color: #fff;
}

.geo-search-selected {
background: lightblue;
}

0 comments on commit 31d391e

Please sign in to comment.