Skip to content
Closed
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
68 changes: 61 additions & 7 deletions src/components/ProjectList.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,54 @@
import ProjectCard from './ProjectCard.astro';
import { projectList } from '../data/projects.js';

// Get all unique tags for filtering
const allTags = [...new Set(projectList.flatMap(project => project.tags || []))].sort();
// Get all unique tags for filtering, ignoring casing so duplicate values share one option
const tagOptions = projectList
.flatMap(project => project.tags || [])
.reduce((tags, tag) => {
const normalizedTag = tag.toLowerCase();

if (!tags.has(normalizedTag)) {
tags.set(normalizedTag, tag);
}

return tags;
}, new Map());
const allTags = [...tagOptions.values()].sort((firstTag, secondTag) =>
firstTag.localeCompare(secondTag, undefined, { sensitivity: 'base' })
);
---

<div id="container">
<div class="inputContainer">
<svg
class="inputIcon"
aria-hidden="true"
focusable="false"
viewBox="0 0 24 24"
>
<circle cx="11" cy="11" r="7"></circle>
<path d="m16 16 4 4"></path>
</svg>
<input
id="search"
type="text"
name="search"
placeholder="Search..."
aria-label="Search"
aria-label="Search projects"
/>
</div>
<div id="tag-selector-container" class="inputContainer">
<select id="tag-selector" aria-labelledby="tag-selector-container">
<svg
class="inputIcon"
aria-hidden="true"
focusable="false"
viewBox="0 0 24 24"
>
<path d="M4 5h16"></path>
<path d="M7 12h10"></path>
<path d="M10 19h4"></path>
</svg>
<select id="tag-selector" aria-label="Filter projects by tag">
<option value="">Filter by tags</option>
{allTags.map(tag => (
<option value={tag.toLowerCase()}>{tag}</option>
Expand Down Expand Up @@ -131,11 +163,29 @@ const allTags = [...new Set(projectList.flatMap(project => project.tags || []))]
.inputContainer {
flex: 1;
min-width: 250px;
position: relative;
}

.inputIcon {
width: 1.125rem;
height: 1.125rem;
position: absolute;
left: 1rem;
top: 50%;
transform: translateY(-50%);
color: rgba(255, 255, 255, 0.68);
fill: none;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
pointer-events: none;
z-index: 1;
}

#search {
width: 100%;
padding: 1rem 1.25rem;
padding: 1rem 1.25rem 1rem 3rem;
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 10px;
background: rgba(255, 255, 255, 0.1);
Expand All @@ -157,7 +207,7 @@ const allTags = [...new Set(projectList.flatMap(project => project.tags || []))]

#tag-selector {
width: 100%;
padding: 1rem 1.25rem;
padding: 1rem 1.25rem 1rem 3rem;
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 10px;
background: rgba(255, 255, 255, 0.1);
Expand Down Expand Up @@ -198,8 +248,12 @@ const allTags = [...new Set(projectList.flatMap(project => project.tags || []))]
min-width: unset;
}

.inputIcon {
left: 0.875rem;
}

#search, #tag-selector {
padding: 0.875rem 1rem;
padding: 0.875rem 1rem 0.875rem 2.75rem;
font-size: 0.95rem;
}

Expand Down