Skip to content

Commit 602c7fb

Browse files
committed
Searching options, better notes again
1 parent 31ed243 commit 602c7fb

2 files changed

Lines changed: 209 additions & 66 deletions

File tree

yark/templates/channel.html

Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,57 @@
8484

8585
.empty-state { text-align: center; color: var(--muted); margin-top: 3rem; }
8686

87+
.search-bar {
88+
display: flex;
89+
justify-content: center;
90+
margin: 1.2rem 0 1rem;
91+
}
92+
93+
.search-bar input {
94+
width: min(24rem, 72vw);
95+
}
96+
97+
.filters {
98+
display: flex;
99+
justify-content: center;
100+
flex-wrap: wrap;
101+
gap: 0.4rem;
102+
margin-bottom: 1.6rem;
103+
}
104+
105+
.filters button {
106+
font-family: inherit;
107+
font-size: 0.82rem;
108+
padding: 0.22rem 0.75rem;
109+
border-radius: 999px;
110+
border: 1px solid rgba(47, 130, 205, 0.22);
111+
background: transparent;
112+
color: var(--accent);
113+
cursor: pointer;
114+
transition: all 0.15s ease;
115+
}
116+
117+
.filters button:hover {
118+
background: rgba(47, 130, 205, 0.08);
119+
border-color: rgba(47, 130, 205, 0.35);
120+
}
121+
122+
.filters button.active {
123+
background: rgba(47, 130, 205, 0.14);
124+
border-color: rgba(47, 130, 205, 0.4);
125+
color: var(--accent);
126+
font-weight: 550;
127+
}
128+
129+
.no-results {
130+
width: 100%;
131+
text-align: center;
132+
color: var(--muted);
133+
margin-top: 2rem;
134+
font-size: 0.88rem;
135+
display: none;
136+
}
137+
87138
@media (prefers-color-scheme: dark) {
88139
.thumbnail { box-shadow: 0 4px 18px -6px rgba(0, 0, 0, 0.4); border-color: rgba(100, 160, 200, 0.15); }
89140
.video-card:hover .thumbnail { box-shadow: 0 12px 34px -14px rgba(0, 0, 0, 0.55); }
@@ -99,10 +150,21 @@
99150
{% block content %}
100151
<h1 class="hero">{{ name }}'s videos</h1>
101152
{% if channel.videos %}
153+
<div class="search-bar">
154+
<input type="text" id="video-search" placeholder="Filter videos…" autocomplete="off">
155+
</div>
156+
<div class="filters">
157+
<button class="active" data-filter="all">All</button>
158+
<button data-filter="newest">Newest</button>
159+
<button data-filter="oldest">Oldest</button>
160+
<button data-filter="popular">Popular</button>
161+
<button data-filter="undownloaded">Not downloaded</button>
162+
</div>
102163
<div id="content">
103164
{% for video in channel.videos %}
104165
{% set downloaded = video.downloaded() %}
105-
<a href="{{ url_for('routes.video', name=name, kind='videos', id=video.id) }}" class="video-card">
166+
<a href="{{ url_for('routes.video', name=name, kind='videos', id=video.id) }}" class="video-card"
167+
data-views="{{ video.views.current() }}" data-uploaded="{{ video.uploaded.timestamp() }}">
106168
<div class="thumbnail">
107169
<img src="{{ url_for('routes.archive_thumbnail', name=name, id=video.thumbnail.current().id) }}"
108170
alt="Thumbnail for {{ video.title.current() }}" loading="lazy" />
@@ -120,13 +182,69 @@ <h1 class="hero">{{ name }}'s videos</h1>
120182
</a>
121183
{% endfor %}
122184
</div>
185+
<p class="no-results" id="no-results">No videos match your search.</p>
123186
{% else %}
124187
<p class="empty-state">No videos found in this archive yet.</p>
125188
{% endif %}
126189
{% endblock %}
127190

128191
{% block scripts %}
129192
<script>
193+
const searchInput = document.getElementById("video-search");
194+
const filterBtns = document.querySelectorAll(".filters button");
195+
const grid = document.getElementById("content");
196+
const noResults = document.getElementById("no-results");
197+
let activeFilter = "all";
198+
199+
function getCards() { return Array.from(grid.querySelectorAll(".video-card")); }
200+
201+
function applyFilters() {
202+
const query = (searchInput ? searchInput.value.toLowerCase().trim() : "");
203+
const cards = getCards();
204+
205+
// -- apply sort / filter --
206+
if (activeFilter === "popular") {
207+
cards.sort(function (a, b) {
208+
return (parseInt(b.getAttribute("data-views")) || 0) - (parseInt(a.getAttribute("data-views")) || 0);
209+
});
210+
} else if (activeFilter === "newest") {
211+
cards.sort(function (a, b) {
212+
return (parseInt(b.getAttribute("data-uploaded")) || 0) - (parseInt(a.getAttribute("data-uploaded")) || 0);
213+
});
214+
} else if (activeFilter === "oldest") {
215+
cards.sort(function (a, b) {
216+
return (parseInt(a.getAttribute("data-uploaded")) || 0) - (parseInt(b.getAttribute("data-uploaded")) || 0);
217+
});
218+
}
219+
220+
// -- reorder in DOM --
221+
cards.forEach(function (c) { grid.appendChild(c); });
222+
223+
// -- show / hide --
224+
let visible = 0;
225+
cards.forEach(function (card) {
226+
var title = (card.querySelector(".title").textContent || "").toLowerCase();
227+
var matchSearch = !query || title.indexOf(query) !== -1;
228+
var matchDownload = activeFilter !== "undownloaded" || card.querySelector(".video-flag");
229+
var show = matchSearch && matchDownload;
230+
card.style.display = show ? "" : "none";
231+
if (show) visible++;
232+
});
233+
234+
if (noResults) noResults.style.display = visible === 0 ? "" : "none";
235+
}
236+
237+
if (searchInput) searchInput.addEventListener("input", applyFilters);
238+
239+
filterBtns.forEach(function (btn) {
240+
btn.addEventListener("click", function () {
241+
filterBtns.forEach(function (b) { b.classList.remove("active"); });
242+
btn.classList.add("active");
243+
activeFilter = btn.getAttribute("data-filter");
244+
applyFilters();
245+
});
246+
});
247+
130248
function setCookie(name, value, days) {
131249
var expires = "";
132250
if (days) {

0 commit comments

Comments
 (0)