mirror of
https://github.com/cssgunc/compass.git
synced 2025-04-06 20:50:17 -04:00
added the filter box and edited the table action to add the filter UI
This commit is contained in:
parent
d689e22dd8
commit
656edbfac7
73
compass/components/FilterBox.tsx
Normal file
73
compass/components/FilterBox.tsx
Normal file
|
@ -0,0 +1,73 @@
|
|||
// FilterBox.tsx
|
||||
import { useState } from "react";
|
||||
|
||||
const mockTags = ["food relief", "period poverty", "nutrition education"];
|
||||
|
||||
export const FilterBox = () => {
|
||||
const [selectedTags, setSelectedTags] = useState<string[]>([]);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
|
||||
const handleTagChange = (tag: string) => {
|
||||
setSelectedTags((prevTags) =>
|
||||
prevTags.includes(tag)
|
||||
? prevTags.filter((t) => t !== tag)
|
||||
: [...prevTags, tag]
|
||||
);
|
||||
};
|
||||
|
||||
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSearchTerm(e.target.value);
|
||||
};
|
||||
|
||||
const renderSelectedTags = () =>
|
||||
selectedTags.map((tag) => (
|
||||
<div
|
||||
key={tag}
|
||||
className="bg-blue-100 text-blue-800 px-2 py-1 rounded-md flex items-center mr-2"
|
||||
>
|
||||
<span>{tag}</span>
|
||||
<span
|
||||
className="ml-2 cursor-pointer"
|
||||
onClick={() => handleTagChange(tag)}
|
||||
>
|
||||
×
|
||||
</span>
|
||||
</div>
|
||||
));
|
||||
|
||||
return (
|
||||
<div className="bg-white border border-gray-300 rounded-md p-2">
|
||||
<div className="mb-2">
|
||||
<span className="font-semibold">Tags contains:</span>
|
||||
|
||||
</div>
|
||||
<div className="flex flex-wrap mb-2 px-2 py-1 border border-gray-300 rounded w-full">
|
||||
{selectedTags.length > 0 && renderSelectedTags()}
|
||||
<input
|
||||
type="text"
|
||||
value={searchTerm}
|
||||
onChange={handleSearchChange}
|
||||
placeholder="Search tags..."
|
||||
|
||||
/>
|
||||
</div>
|
||||
<div className="max-h-48 overflow-y-auto">
|
||||
{mockTags
|
||||
.filter((tag) =>
|
||||
tag.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
)
|
||||
.map((tag) => (
|
||||
<div key={tag} className="flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={selectedTags.includes(tag)}
|
||||
onChange={() => handleTagChange(tag)}
|
||||
className="mr-2"
|
||||
/>
|
||||
<label>{tag}</label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -113,6 +113,7 @@ export const Table = () => {
|
|||
}
|
||||
|
||||
// TODO: Filtering
|
||||
|
||||
// TODO: Sorting
|
||||
|
||||
const table = useReactTable({
|
||||
|
|
|
@ -1,46 +1,64 @@
|
|||
/** The actions (Filter, Sort, Search) at the top of the table. */
|
||||
// TableAction.tsx
|
||||
import { MagnifyingGlassIcon } from "@heroicons/react/24/solid";
|
||||
import { ChangeEventHandler, Dispatch, FunctionComponent, SetStateAction, useRef, useState } from "react";
|
||||
import { ChangeEventHandler, FunctionComponent, useRef, useState } from "react";
|
||||
import { FilterBox } from "../../components/FilterBox";
|
||||
|
||||
type TableActionProps = {
|
||||
query: string
|
||||
handleChange: ChangeEventHandler<HTMLInputElement>
|
||||
}
|
||||
query: string;
|
||||
handleChange: ChangeEventHandler<HTMLInputElement>;
|
||||
};
|
||||
|
||||
export const TableAction: FunctionComponent<TableActionProps> = ({query, handleChange}) => {
|
||||
export const TableAction: FunctionComponent<TableActionProps> = ({
|
||||
query,
|
||||
handleChange,
|
||||
}) => {
|
||||
const searchInput = useRef<HTMLInputElement>(null);
|
||||
const [searchActive, setSearchActive] = useState(false);
|
||||
const [showFilterBox, setShowFilterBox] = useState(false);
|
||||
|
||||
const activateSearch = () => {
|
||||
setSearchActive(true);
|
||||
if (searchInput.current === null) { return; }
|
||||
if (searchInput.current === null) {
|
||||
return;
|
||||
}
|
||||
searchInput.current.focus();
|
||||
searchInput.current.addEventListener("focusout", () => {
|
||||
if (searchInput.current?.value.trim() === "") {
|
||||
searchInput.current.value = "";
|
||||
deactivateSearch();
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const deactivateSearch = () => setSearchActive(false);
|
||||
|
||||
const toggleFilterBox = () => setShowFilterBox((prev) => !prev);
|
||||
|
||||
return (
|
||||
<div className="w-auto flex flex-row gap-x-0.5 items-center justify-between text-xs font-medium text-gray-500 p-2">
|
||||
<span className="p-1 rounded hover:bg-gray-100">Filter</span>
|
||||
<span
|
||||
className="p-1 rounded hover:bg-gray-100"
|
||||
onClick={toggleFilterBox}
|
||||
>
|
||||
Filter
|
||||
</span>
|
||||
{showFilterBox && <FilterBox />}
|
||||
<span className="p-1 rounded hover:bg-gray-100">Sort</span>
|
||||
<span className="p-1 rounded hover:bg-gray-100" onClick={() => activateSearch()}>
|
||||
<span className="p-1 rounded hover:bg-gray-100" onClick={activateSearch}>
|
||||
<MagnifyingGlassIcon className="w-4 h-4 inline" />
|
||||
</span>
|
||||
<input
|
||||
ref={searchInput}
|
||||
className={"outline-none transition-all duration-300 " + (searchActive ? "w-48" : "w-0")}
|
||||
className={
|
||||
"outline-none transition-all duration-300 " +
|
||||
(searchActive ? "w-48" : "w-0")
|
||||
}
|
||||
type="text"
|
||||
name="search"
|
||||
placeholder="Type to search..."
|
||||
value={query ?? ""}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
};
|
6
package-lock.json
generated
Normal file
6
package-lock.json
generated
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "compass",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
Loading…
Reference in New Issue
Block a user