mirror of
https://github.com/cssgunc/compass.git
synced 2025-04-09 14:00:15 -04:00
Create search bar and filter pills
Not tied to any functionality at the moment, however
This commit is contained in:
parent
0ae107ff1c
commit
b2ffd46a79
|
@ -1,18 +1,38 @@
|
|||
import { FunnelIcon as FunnelIconOutline } from "@heroicons/react/24/outline";
|
||||
import {
|
||||
ArchiveBoxIcon,
|
||||
ChevronDownIcon,
|
||||
FunnelIcon,
|
||||
MagnifyingGlassIcon,
|
||||
TagIcon,
|
||||
XMarkIcon,
|
||||
} from "@heroicons/react/24/solid";
|
||||
import React, { useState } from "react";
|
||||
import React, {
|
||||
ReactNode,
|
||||
SetStateAction,
|
||||
useState,
|
||||
useRef,
|
||||
useEffect,
|
||||
} from "react";
|
||||
import Image from "next/image";
|
||||
import { FilterBox } from "../FilterBox";
|
||||
|
||||
export const LandingSearchBar: React.FC = () => {
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [showFilters, setShowFilters] = useState(false);
|
||||
const [showFilterBox, setShowFilterBox] = useState(false);
|
||||
const toggleFilterBox = () => setShowFilterBox((prev) => !prev);
|
||||
|
||||
const collections = ["Resources", "Services"];
|
||||
const [selectedCollections, setSelectedCollections] = useState(
|
||||
new Array(collections.length).fill(false)
|
||||
);
|
||||
|
||||
const tags = ["Food Relief", "Period Poverty", "Nutrition Education"];
|
||||
const [selectedTags, setSelectedTags] = useState(
|
||||
new Array(tags.length).fill(false)
|
||||
);
|
||||
|
||||
const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSearchTerm(event.target.value);
|
||||
};
|
||||
|
@ -57,19 +77,39 @@ export const LandingSearchBar: React.FC = () => {
|
|||
<button
|
||||
className={
|
||||
"hover:bg-purple-100 rounded-md p-1 " +
|
||||
(showFilterBox ? "bg-purple-100" : "")
|
||||
(showFilters ? "bg-purple-100" : "")
|
||||
}
|
||||
onClick={toggleFilterBox}
|
||||
onClick={() => setShowFilters(!showFilters)}
|
||||
>
|
||||
{!showFilterBox && (
|
||||
{!showFilters && (
|
||||
<FunnelIconOutline className="h-5 w-5 text-gray-500" />
|
||||
)}
|
||||
{showFilterBox && (
|
||||
{showFilters && (
|
||||
<FunnelIcon className="h-5 w-5 text-purple-800" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Search filters */}
|
||||
<div className={"mt-2 flex " + (showFilters ? "" : "hidden")}>
|
||||
<FilterPill
|
||||
icon={ArchiveBoxIcon}
|
||||
name="In"
|
||||
options={collections}
|
||||
selectedOptions={selectedCollections}
|
||||
setSelectedOptions={setSelectedCollections}
|
||||
/>
|
||||
<FilterPill
|
||||
icon={TagIcon}
|
||||
name="Tags"
|
||||
options={tags}
|
||||
selectedOptions={selectedTags}
|
||||
setSelectedOptions={setSelectedTags}
|
||||
searchBar
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* search results, for now since it's empty this is the default screen */}
|
||||
<div className="flex flex-col pt-16 space-y-2 justify-center items-center">
|
||||
<Image
|
||||
|
@ -86,3 +126,129 @@ export const LandingSearchBar: React.FC = () => {
|
|||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Closes the filter dropdown when the user clicks outside the dropdown.
|
||||
const useFilterPillDropdown = (
|
||||
ref: React.RefObject<HTMLDivElement>,
|
||||
setShowDropdown: Function
|
||||
) => {
|
||||
// Close on outside click
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (ref.current && !ref.current.contains(event.target as Node)) {
|
||||
if (setShowDropdown) setShowDropdown(false);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("mousedown", handleClickOutside);
|
||||
return () =>
|
||||
// Unbind the event listener on cleanup.
|
||||
document.removeEventListener("mousedown", handleClickOutside);
|
||||
}, [ref, setShowDropdown]);
|
||||
};
|
||||
|
||||
interface FilterPillProps {
|
||||
icon: React.ForwardRefExoticComponent<
|
||||
Omit<React.SVGProps<SVGSVGElement>, "ref">
|
||||
>;
|
||||
name: string;
|
||||
searchBar?: boolean;
|
||||
options: string[];
|
||||
selectedOptions: boolean[];
|
||||
setSelectedOptions: React.Dispatch<SetStateAction<boolean[]>>;
|
||||
}
|
||||
|
||||
const FilterPill: React.FC<FilterPillProps> = ({
|
||||
icon,
|
||||
name,
|
||||
options,
|
||||
selectedOptions,
|
||||
setSelectedOptions,
|
||||
searchBar = false,
|
||||
}) => {
|
||||
const Icon = icon;
|
||||
const [showDropdown, setShowDropdown] = useState(false);
|
||||
const [isActive, setIsActive] = useState(false);
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const handleCheck = (
|
||||
e: React.ChangeEvent<HTMLInputElement>,
|
||||
item: string
|
||||
) => {
|
||||
const selected = selectedOptions.map((o, i) => {
|
||||
if (i == options.indexOf(item)) {
|
||||
return e.target.checked;
|
||||
} else {
|
||||
return o;
|
||||
}
|
||||
});
|
||||
|
||||
setSelectedOptions(selected);
|
||||
setIsActive(selected.includes(true));
|
||||
};
|
||||
|
||||
// Closes dropdown when clicked outside
|
||||
useFilterPillDropdown(dropdownRef, setShowDropdown);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
"border rounded-2xl w-max px-2 py-0.5 text-sm mr-2 relative " +
|
||||
(isActive
|
||||
? "border-purple-800 text-purple-800 bg-purple-100"
|
||||
: "border-gray-400 text-gray-400 hover:bg-gray-100")
|
||||
}
|
||||
>
|
||||
{/* The filter pill */}
|
||||
<button
|
||||
className="flex gap-1 items-center"
|
||||
onClick={() => setShowDropdown(!showDropdown)}
|
||||
>
|
||||
<Icon className="h-4 w-4" />
|
||||
<span className="max-w-36 overflow-hidden whitespace-nowrap text-ellipsis">
|
||||
{name}
|
||||
{/* Displaying the selected options */}
|
||||
{options.reduce(
|
||||
(a, b, i) =>
|
||||
a +
|
||||
(selectedOptions[i] // If selected, append option
|
||||
? (a === "" ? ": " : ", ") + b // (prepend ": " if first element, ", " if not)
|
||||
: ""),
|
||||
""
|
||||
)}
|
||||
</span>
|
||||
<ChevronDownIcon className="h-4 w-4" />
|
||||
</button>
|
||||
|
||||
{/* The filter option selection dropdown */}
|
||||
<div
|
||||
ref={dropdownRef}
|
||||
className={
|
||||
"absolute top-full mt-0.5 left-0 border border-gray-200 bg-white shadow-lg rounded-md p-1.5 w-48 " +
|
||||
(showDropdown ? "flex flex-col" : "hidden")
|
||||
}
|
||||
>
|
||||
<input
|
||||
className="border w-full rounded-md mb-1 text-gray-600 p-1"
|
||||
type="text"
|
||||
placeholder={"Search " + name.toLowerCase()}
|
||||
hidden={!searchBar}
|
||||
/>
|
||||
{options.map((item, index) => {
|
||||
return (
|
||||
<label className="text-gray-800" key={index}>
|
||||
<input
|
||||
type="checkbox"
|
||||
name="name"
|
||||
className="mr-1"
|
||||
checked={selectedOptions[index]}
|
||||
onChange={(e) => handleCheck(e, item)}
|
||||
/>
|
||||
{item}
|
||||
</label>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue
Block a user