Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/assets/star-filled.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions src/components/CharacterCardSmall.css
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
}

.star-favorite {
filter: brightness(0) saturate(100%) invert(84%) sepia(85%) saturate(829%) hue-rotate(359deg) brightness(105%) contrast(104%);
filter: none;
}

.details-button {
Expand Down Expand Up @@ -120,4 +120,4 @@
.card-content {
width: min(100%, 12rem);
}
}
}
3 changes: 2 additions & 1 deletion src/components/CharacterCardSmall.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import "./CharacterCardSmall.css";
import CardShape from "../assets/card-shape.svg";
import StarShape from "../assets/star.svg";
import StarFilledShape from "../assets/star-filled.svg";

type CharacterCardSmallProps = {
name: string;
Expand Down Expand Up @@ -44,7 +45,7 @@ export default function CharacterCardSmall({
}
>
<img
src={StarShape}
src={isFavorite ? StarFilledShape : StarShape}
alt=""
className={`star ${isFavorite ? "star-favorite" : ""}`}
/>
Expand Down
3 changes: 1 addition & 2 deletions src/components/CharacterDetailCard.css
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,7 @@
}

.detail-star-favorite {
filter: brightness(0) saturate(100%) invert(84%) sepia(85%) saturate(829%)
hue-rotate(359deg) brightness(105%) contrast(104%);
filter: none;
}

@media (max-width: 750px) {
Expand Down
3 changes: 2 additions & 1 deletion src/components/CharacterDetailCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import ArrowRight from "../assets/arrow-right.svg";
import UserRound from "../assets/user-round.svg";
import X from "../assets/X.svg";
import Star from "../assets/star.svg";
import StarFilled from "../assets/star-filled.svg";

type CharacterDetailCardProps = {
character: Character;
Expand Down Expand Up @@ -99,7 +100,7 @@ export default function CharacterDetailCard({
}
>
<img
src={Star}
src={isFavorite ? StarFilled : Star}
alt=""
className={`detail-star ${
isFavorite ? "detail-star-favorite" : ""
Expand Down
72 changes: 61 additions & 11 deletions src/pages/CharactersPage.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,7 @@
.filter-area {
order: 1;
width: 100%;
padding: 1rem;
box-sizing: border-box;
border: 2px solid #3f3f3f;
border-radius: 1rem;
}

@media (min-width: 768px) {
Expand All @@ -42,6 +39,8 @@
height: fit-content;
margin-right: 1rem;
padding: 2rem;
border: 2px solid #3f3f3f;
border-radius: 1rem;
}
}

Expand Down Expand Up @@ -113,13 +112,59 @@
margin-bottom: 1rem;
}

.filter-area>.filter-group {
/* The CSS for the dropdown menu was made with help from AI */
.filter-dropdown {
border: 2px solid #3f3f3f;
border-radius: 0.5rem;
background: #282828;
}

.filter-dropdown summary {
display: flex;
align-items: center;
justify-content: space-between;
min-height: 2.75rem;
padding: 0 1rem;
font-weight: 700;
color: #f3f4f6;
cursor: pointer;
list-style: none;
}

.filter-dropdown summary::-webkit-details-marker {
display: none;
}

.filter-dropdown-icon {
display: inline-flex;
width: 0.6rem;
height: 0.6rem;
border-right: 2px solid currentColor;
border-bottom: 2px solid currentColor;
transform: rotate(45deg);
transition: transform 160ms ease;
}

.filter-dropdown[open] .filter-dropdown-icon {
transform: rotate(225deg);
}

.filter-options,
.filter-sidebar {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0.75rem;
margin: 0;
}

.filter-options {
padding: 0 1rem 1rem;
}

.filter-sidebar {
display: none;
}

.filter-group legend {
font-weight: 700;
}
Expand All @@ -134,8 +179,12 @@
}

@media (min-width: 768px) {
.filter-area>.filter-group {
grid-template-columns: 1fr;
.filter-dropdown {
display: none;
}

.filter-sidebar {
display: block;
}

.search-button {
Expand All @@ -147,13 +196,14 @@
repeat(auto-fill, minmax(min(100%, 12rem), 1fr));
}

.filter-area>.filter-group {
display: block;
.filter-options,
.filter-sidebar {
grid-template-columns: 1fr;
}
}

@media (max-width: 420px) {
.filter-area>.filter-group {
.filter-options {
grid-template-columns: 1fr;
}
}
}
69 changes: 42 additions & 27 deletions src/pages/CharactersPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ import {
} from "../utils/filterCharacters";

const filters: { title: string; key: FilterCategory; options: string[] }[] = [
{ title: "Favorite", key: "favorite", options: ["My favorites"] },
{ title: "Gender", key: "gender", options: ["Female", "Male", "Unknown"] },
{ title: "Species", key: "species", options: ["Human", "Alien", "Unknown"] },
{ title: "Status", key: "status", options: ["Alive", "Dead", "Unknown"] },
{ title: "Status", key: "status", options: ["Alive", "Dead", "Unknown"] }
];

// AI was used to create the fetchCharacters function
Expand Down Expand Up @@ -51,6 +52,7 @@ export default function CharactersPage() {
}

return {
favorite: [],
gender: [],
species: [],
status: [],
Expand Down Expand Up @@ -85,6 +87,7 @@ export default function CharactersPage() {
characters,
searchQuery,
selectedFilters,
favoriteCharacterIds,
);

function handleFilterChange(
Expand All @@ -110,6 +113,35 @@ export default function CharactersPage() {
);
}

// Changed with help from AI as it needed fitting for dropdown.
function renderFilterOptions() {
return filters.map((filter) => (
<fieldset key={filter.title} className="filter-group">
<legend>{filter.title}</legend>
{filter.options.map((option) => (
<label key={option} className="checkbox-option">
<input
type="checkbox"
name={filter.key}
value={option.toLowerCase()}
checked={selectedFilters[filter.key].includes(
option.toLowerCase(),
)}
onChange={(event) =>
handleFilterChange(
filter.key,
event.target.value,
event.target.checked,
)
}
/>{" "}
{option}
</label>
))}
</fieldset>
));
}

function showPreviousCharacter() {
if (!selectedCharacter) return;

Expand Down Expand Up @@ -158,32 +190,15 @@ export default function CharactersPage() {
</section>
</section>
<div className="filter-area" aria-label="Filters">
<aside className="filter-group">
{filters.map((filter) => (
<fieldset key={filter.title} className="filter-group">
<legend>{filter.title}</legend>
{filter.options.map((option) => (
<label key={option} className="checkbox-option">
<input
type="checkbox"
name={filter.key}
value={option.toLowerCase()}
checked={selectedFilters[filter.key].includes(
option.toLowerCase(),
)}
onChange={(event) =>
handleFilterChange(
filter.key,
event.target.value,
event.target.checked,
)
}
/>{" "}
{option}
</label>
))}
</fieldset>
))}
<details className="filter-dropdown">
<summary>
Filters
<span className="filter-dropdown-icon" aria-hidden="true" />
</summary>
<div className="filter-options">{renderFilterOptions()}</div>
</details>
<aside className="filter-sidebar">
{renderFilterOptions()}
</aside>
</div>
{selectedCharacter && (
Expand Down
14 changes: 8 additions & 6 deletions src/utils/filterCharacters.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import type { Character } from "../types/Character";

export type FilterCategory = "gender" | "species" | "status";
export type FilterCategory = "favorite" | "gender" | "species" | "status";
export type SelectedFilters = Record<FilterCategory, string[]>;

export function filterCharacters(
// Takes in all characters, the value in the search field and the chosen filters
characters: Character[],
searchQuery: string,
selectedFilters: SelectedFilters,
// Takes in all characters, the value in the search field and the chosen filters
characters: Character[], searchQuery: string, selectedFilters: SelectedFilters, favoriteCharacterIds: number[],
): Character[] {
return characters.filter((character) => {
//Checks whether each character's name matches with the search and if the character's gender, specie or status is checked off in the filter
const matchesSearch = character.name
.toLowerCase()
.includes(searchQuery.toLowerCase());

const matchesFavorite =
selectedFilters.favorite.length === 0 ||
favoriteCharacterIds.includes(character.id);

const matchesGender =
selectedFilters.gender.length === 0 ||
selectedFilters.gender.includes(character.gender.toLowerCase());
Expand All @@ -27,6 +29,6 @@ export function filterCharacters(
selectedFilters.status.length === 0 ||
selectedFilters.status.includes(character.status.toLowerCase());
//Returns the result of the checks mentioned above
return matchesSearch && matchesGender && matchesSpecies && matchesStatus;
return matchesSearch && matchesFavorite && matchesGender && matchesSpecies && matchesStatus;
});
}