Skip to content

Commit 130f6e7

Browse files
committed
add sorting by the supervisor
1 parent b36fe39 commit 130f6e7

File tree

4 files changed

+98
-23
lines changed

4 files changed

+98
-23
lines changed

_includes/project-list.html

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,18 @@
1515
{% if availability_value == false or availability_value == 0 or availability_value == nil %}
1616
{% assign is_unavailable = true %}
1717
{% endif %}
18-
<li class="project-card{% if is_unavailable %} is-unavailable{% endif %}" id="{{ project_id }}" data-tags="{{ normalized_tags }}"{% if is_unavailable %} aria-disabled="true"{% endif %}>
18+
{% assign supervisor_name = '' %}
19+
{% if project.supervisor and project.supervisor.name %}
20+
{% assign supervisor_name = project.supervisor.name | strip %}
21+
{% endif %}
22+
{% assign normalized_supervisor = supervisor_name | downcase %}
23+
<li class="project-card{% if is_unavailable %} is-unavailable{% endif %}" id="{{ project_id }}" data-tags="{{ normalized_tags }}" data-supervisor="{{ normalized_supervisor }}"{% if is_unavailable %} aria-disabled="true"{% endif %}>
1924
<div class="project-card__header">
2025
<h2>{{ project.title }}</h2>
2126
{% if project.supervisor %}
2227
<p class="project-card__supervisor">
2328
<strong>Supervisor:</strong>
24-
{{ project.supervisor.name }}
29+
<a href="#" class="project-card__supervisor-link" data-supervisor="{{ normalized_supervisor }}" data-supervisor-control data-scroll-to-filter="true">{{ project.supervisor.name }}</a>
2530
{% if project.supervisor.email %}
2631
· <a href="mailto:{{ project.supervisor.email }}">{{ project.supervisor.email }}</a>
2732
{% endif %}

_includes/project-section.html

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,34 @@
1919
{% assign tag_list = normalized_tags | split: ',' | uniq | sort %}
2020
{% endif %}
2121

22+
{% assign flattened_supervisors = projects | map: 'supervisor' | compact %}
23+
{% assign supervisor_names = '' %}
24+
{% for supervisor in flattened_supervisors %}
25+
{% assign supervisor_name = supervisor.name | strip %}
26+
{% if supervisor_name != '' %}
27+
{% if supervisor_names == '' %}
28+
{% assign supervisor_names = supervisor_name %}
29+
{% else %}
30+
{% assign supervisor_names = supervisor_names | append: ',' | append: supervisor_name %}
31+
{% endif %}
32+
{% endif %}
33+
{% endfor %}
34+
{% assign supervisor_list = '' %}
35+
{% if supervisor_names != '' %}
36+
{% assign supervisor_list = supervisor_names | split: ',' | uniq | sort_natural %}
37+
{% endif %}
38+
2239
<section class="project-section" data-project-section>
2340
<div class="project-section__header">
2441
{% if include.heading %}
2542
<h2 class="project-section__title">{{ include.heading }}</h2>
2643
{% endif %}
27-
<p class="muted project-section__intro">Narrow the projects down using the tags below.</p>
44+
<p class="muted project-section__intro">Narrow the projects down by supervisor or tag.</p>
2845
</div>
2946

3047
{% if tag_list != '' and tag_list != empty %}
3148
<div class="tag-filter" data-tag-filter>
32-
<button type="button" class="tag-filter__btn is-active" data-tag="all" data-filter-control>All</button>
49+
<button type="button" class="tag-filter__btn is-active" data-tag="all" data-filter-control>All tags</button>
3350
{% for tag in tag_list %}
3451
{% if tag != '' %}
3552
<button type="button" class="tag-filter__btn" data-tag="{{ tag }}" data-filter-control>{{ tag }}</button>
@@ -38,9 +55,20 @@ <h2 class="project-section__title">{{ include.heading }}</h2>
3855
</div>
3956
{% endif %}
4057

41-
{% if has_projects %}
42-
{% include project-list.html projects=projects %}
43-
{% else %}
44-
<p>No projects are listed yet. Check back soon!</p>
45-
{% endif %}
58+
{% if supervisor_list != '' and supervisor_list != empty %}
59+
<div class="tag-filter" data-supervisor-filter>
60+
<a href="#" class="tag-filter__btn is-active" data-supervisor="all" data-supervisor-control>All supervisors</a>
61+
{% for supervisor in supervisor_list %}
62+
{% if supervisor != '' %}
63+
<a href="#" class="tag-filter__btn" data-supervisor="{{ supervisor | downcase }}" data-supervisor-control>{{ supervisor }}</a>
64+
{% endif %}
65+
{% endfor %}
66+
</div>
67+
{% endif %}
68+
69+
{% if has_projects %}
70+
{% include project-list.html projects=projects %}
71+
{% else %}
72+
<p>No projects are listed yet. Check back soon!</p>
73+
{% endif %}
4674
</section>

assets/css/projects.css

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,9 @@ main {
209209
border-radius: 999px;
210210
cursor: pointer;
211211
font-size: 0.95rem;
212+
text-decoration: none;
213+
display: inline-flex;
214+
align-items: center;
212215
transition: all 0.2s ease;
213216
}
214217

@@ -292,11 +295,16 @@ main {
292295
font-size: 0.95rem;
293296
}
294297

298+
.project-card__supervisor-link a {
299+
color: var(--text);
300+
}
301+
295302
.project-card__supervisor a {
296-
color: var(--accent);
303+
color: var(--text);
297304
text-decoration: none;
298305
}
299306

307+
300308
.project-card__supervisor a:hover,
301309
.project-card__supervisor a:focus-visible {
302310
text-decoration: underline;

assets/js/tag-filter.js

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,29 @@ const initTagFilter = () => {
33

44
sections.forEach((section) => {
55
const filterContainer = section.querySelector("[data-tag-filter]");
6+
const supervisorFilterContainer = section.querySelector("[data-supervisor-filter]");
67
const cards = Array.from(section.querySelectorAll(".project-card"));
78

89
if (!cards.length) return;
910

1011
let activeTag = "all";
12+
let activeSupervisor = "all";
1113

12-
const captureButtons = () =>
14+
const captureTagButtons = () =>
1315
Array.from(section.querySelectorAll("[data-tag][data-filter-control]"));
16+
const captureSupervisorButtons = () =>
17+
Array.from(section.querySelectorAll("[data-supervisor][data-supervisor-control]"));
1418

15-
const setActiveButtonState = (tag) => {
16-
captureButtons().forEach((btn) => {
17-
const isFilterButton = !!btn.closest("[data-tag-filter]");
19+
const setActiveButtonState = (type, value) => {
20+
const buttons = type === "supervisor" ? captureSupervisorButtons() : captureTagButtons();
21+
const containerSelector = type === "supervisor" ? "[data-supervisor-filter]" : "[data-tag-filter]";
22+
const dataKey = type === "supervisor" ? "supervisor" : "tag";
23+
24+
buttons.forEach((btn) => {
25+
const isFilterButton = !!btn.closest(containerSelector);
1826
if (!isFilterButton) return;
1927

20-
if (btn.dataset.tag === tag) {
28+
if (btn.dataset[dataKey] === value) {
2129
btn.classList.add("is-active");
2230
btn.setAttribute("aria-pressed", "true");
2331
} else {
@@ -27,13 +35,17 @@ const initTagFilter = () => {
2735
});
2836
};
2937

30-
const applyFilter = (tag) => {
38+
const applyFilter = () => {
3139
cards.forEach((card) => {
3240
const datasetTags = (card.dataset.tags || "")
3341
.split(",")
3442
.map((t) => t.trim())
3543
.filter(Boolean);
36-
const matches = tag === "all" ? true : datasetTags.includes(tag);
44+
const datasetSupervisor = (card.dataset.supervisor || "").trim();
45+
const matchesTag = activeTag === "all" ? true : datasetTags.includes(activeTag);
46+
const matchesSupervisor =
47+
activeSupervisor === "all" ? true : datasetSupervisor === activeSupervisor;
48+
const matches = matchesTag && matchesSupervisor;
3749
card.classList.toggle("is-hidden", !matches);
3850
card.setAttribute("aria-hidden", String(!matches));
3951
});
@@ -42,24 +54,46 @@ const initTagFilter = () => {
4254
const handleTagSelection = (tag, options = {}) => {
4355
if (!tag || tag === activeTag) return;
4456
activeTag = tag;
45-
setActiveButtonState(tag);
46-
applyFilter(tag);
57+
setActiveButtonState("tag", tag);
58+
applyFilter();
4759

4860
if (filterContainer && options.scrollToFilter) {
4961
filterContainer.scrollIntoView({ behavior: "smooth", block: "start" });
5062
}
5163
};
5264

53-
captureButtons().forEach((button) => {
54-
button.addEventListener("click", () => {
65+
const handleSupervisorSelection = (supervisor, options = {}) => {
66+
if (!supervisor || supervisor === activeSupervisor) return;
67+
activeSupervisor = supervisor;
68+
setActiveButtonState("supervisor", supervisor);
69+
applyFilter();
70+
71+
if (supervisorFilterContainer && options.scrollToFilter) {
72+
supervisorFilterContainer.scrollIntoView({ behavior: "smooth", block: "start" });
73+
}
74+
};
75+
76+
captureTagButtons().forEach((button) => {
77+
button.addEventListener("click", (event) => {
78+
event.preventDefault();
5579
handleTagSelection(button.dataset.tag, {
5680
scrollToFilter: button.dataset.scrollToFilter === "true",
5781
});
5882
});
5983
});
6084

61-
setActiveButtonState(activeTag);
62-
applyFilter(activeTag);
85+
captureSupervisorButtons().forEach((button) => {
86+
button.addEventListener("click", (event) => {
87+
event.preventDefault();
88+
handleSupervisorSelection(button.dataset.supervisor, {
89+
scrollToFilter: button.dataset.scrollToFilter === "true",
90+
});
91+
});
92+
});
93+
94+
setActiveButtonState("tag", activeTag);
95+
setActiveButtonState("supervisor", activeSupervisor);
96+
applyFilter();
6397
});
6498
};
6599

0 commit comments

Comments
 (0)