First edition

This commit is contained in:
2026-05-27 16:55:56 -06:00
commit b9d07f7a7c
6 changed files with 967 additions and 0 deletions

114
app.js Normal file
View File

@@ -0,0 +1,114 @@
const projects = window.projects || [];
const featuredContainer = document.querySelector("#featured-projects");
const grid = document.querySelector("#project-grid");
const emptyState = document.querySelector("#empty-state");
const searchInput = document.querySelector("#project-search");
const filters = Array.from(document.querySelectorAll(".filter"));
const projectCount = document.querySelector("#project-count");
let activeFilter = "all";
projectCount.textContent = projects.length;
function formatDate(value) {
return new Intl.DateTimeFormat("en", {
month: "short",
year: "numeric"
}).format(new Date(`${value}T00:00:00`));
}
function matchesFilter(project) {
if (activeFilter === "all") return true;
if (activeFilter === "featured") return project.tags.includes("featured");
return project.language === activeFilter || project.tags.includes(activeFilter);
}
function matchesSearch(project) {
const query = searchInput.value.trim().toLowerCase();
if (!query) return true;
const haystack = [
project.name,
project.description,
project.siteUrl,
project.language,
project.license,
project.tags.join(" ")
].join(" ").toLowerCase();
return haystack.includes(query);
}
function metric(label, value) {
return `<span><strong>${value}</strong>${label}</span>`;
}
function tags(project) {
return project.tags
.filter((tag) => tag !== "featured")
.slice(0, 3)
.map((tag) => `<span class="tag">${tag}</span>`)
.join("");
}
function actions(project) {
const liveLink = project.siteUrl
? `<a class="project-link live" href="${project.siteUrl}">Launch</a>`
: "";
return `
<span class="project-actions">
${liveLink}
<a class="project-link" href="${project.url}">GitHub</a>
</span>
`;
}
function card(project, size = "standard") {
return `
<article class="project-card ${size}" style="--accent: ${project.accent}">
<div class="project-accent" aria-hidden="true"></div>
<div class="project-body">
<div class="project-title-row">
<h3>${project.name}</h3>
<span class="language">${project.language || "Code"}</span>
</div>
<p>${project.description}</p>
<div class="tag-row">${tags(project)}</div>
</div>
<footer class="project-meta">
${metric("stars", project.stars)}
${metric("forks", project.forks)}
<span>Updated ${formatDate(project.updated)}</span>
${actions(project)}
</footer>
</article>
`;
}
function renderFeatured() {
featuredContainer.innerHTML = projects
.filter((project) => project.tags.includes("featured"))
.map((project) => card(project, "featured-card"))
.join("");
}
function renderProjects() {
const visible = projects.filter((project) => matchesFilter(project) && matchesSearch(project));
grid.innerHTML = visible.map((project) => card(project)).join("");
emptyState.hidden = visible.length > 0;
}
filters.forEach((button) => {
button.addEventListener("click", () => {
filters.forEach((filter) => filter.classList.remove("active"));
button.classList.add("active");
activeFilter = button.dataset.filter;
renderProjects();
});
});
searchInput.addEventListener("input", renderProjects);
renderFeatured();
renderProjects();