Skip to content

Commit c71187c

Browse files
authored
Story 2137: Post Filter and Library Filter Components (boostorg#2260)
1 parent cdbe296 commit c71187c

12 files changed

Lines changed: 710 additions & 34 deletions

File tree

core/views.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,6 +1168,15 @@ def get_context_data(self, **kwargs):
11681168
("math", "Math & Numerics"),
11691169
("networking", "Networking"),
11701170
]
1171+
context["demo_combo_multi_tags"] = [
1172+
("algorithms", "Algorithms"),
1173+
("containers", "Containers"),
1174+
("io", "I/O"),
1175+
("math", "Math & Numerics"),
1176+
("networking", "Networking"),
1177+
("testing", "Testing"),
1178+
("concurrency", "Concurrency"),
1179+
]
11711180
badge_img = f"{settings.STATIC_URL}img/v3/badges"
11721181
context["badge_icon_srcs"] = [
11731182
f"{badge_img}/badge-first-place.png",
@@ -1211,6 +1220,17 @@ def get_context_data(self, **kwargs):
12111220

12121221
context["demo_badges_few"] = context["demo_badges"][:3]
12131222

1223+
context["post_filter_options"] = [
1224+
{"label": "All", "value": "all"},
1225+
{"label": "News", "value": "news"},
1226+
{"label": "Blog", "value": "blog"},
1227+
{"label": "Links", "value": "links"},
1228+
{"label": "Videos", "value": "videos"},
1229+
{"label": "Discussions", "value": "discussions"},
1230+
{"label": "Achievements", "value": "achievements"},
1231+
{"label": "Issues", "value": "issues"},
1232+
]
1233+
12141234
context["demo_posts"] = [
12151235
{
12161236
"title": "A talk by Richard Thomson at the Utah C++ Programmers Group",
@@ -1266,6 +1286,94 @@ def get_context_data(self, **kwargs):
12661286
"flag_emoji": "🇺🇸",
12671287
}
12681288

1289+
cpp_options = [
1290+
("all", "All"),
1291+
("cpp03", "C++03"),
1292+
("cpp11", "C++11"),
1293+
("cpp14", "C++14"),
1294+
("cpp17", "C++17"),
1295+
("cpp20", "C++20"),
1296+
("cpp23", "C++23"),
1297+
]
1298+
context["library_filter_fields"] = [
1299+
{
1300+
"type": "dropdown",
1301+
"name": "view",
1302+
"label": "View",
1303+
"options": [
1304+
("list", "List"),
1305+
("grid", "Grid"),
1306+
("category", "Category"),
1307+
("grading", "Grading"),
1308+
],
1309+
"selected": "list",
1310+
"width": "narrow",
1311+
},
1312+
{
1313+
"type": "dropdown",
1314+
"name": "grading",
1315+
"label": "Grading",
1316+
"options": [
1317+
("all", "All"),
1318+
("flagship", "Flagship"),
1319+
("core", "Core"),
1320+
("deprecated", "Deprecated"),
1321+
("legacy", "Legacy"),
1322+
],
1323+
"selected": "all",
1324+
"width": "wide",
1325+
},
1326+
{
1327+
"type": "dropdown",
1328+
"name": "min_cpp",
1329+
"label": "Min. C++ Version",
1330+
"options": cpp_options,
1331+
"selected": "all",
1332+
"width": "narrow",
1333+
},
1334+
{
1335+
"type": "dropdown",
1336+
"name": "max_cpp",
1337+
"label": "Max. C++ Version",
1338+
"options": cpp_options,
1339+
"selected": "all",
1340+
"width": "narrow",
1341+
},
1342+
{
1343+
"type": "combo_multi",
1344+
"name": "category",
1345+
"label": "Category",
1346+
"options": [
1347+
("algorithms", "Algorithms"),
1348+
("asynchronous", "Asynchronous"),
1349+
("awaitables", "Awaitables"),
1350+
("containers", "Containers"),
1351+
("coroutines", "Coroutines"),
1352+
("correctness", "Correctness"),
1353+
# More dummy data to show scrollbar
1354+
("data_processing", "Data processing"),
1355+
("debugging", "Debugging"),
1356+
("file_systems", "File systems"),
1357+
("formatting", "Formatting"),
1358+
("graphics", "Graphics"),
1359+
],
1360+
"width": "wide",
1361+
"placeholder": "Search",
1362+
},
1363+
{
1364+
"type": "dropdown",
1365+
"name": "sort",
1366+
"label": "Sort by",
1367+
"options": [
1368+
("alphabetical", "Alphabetical"),
1369+
("popular", "Most Popular"),
1370+
("updated", "Recently Updated"),
1371+
("release", "Release Date"),
1372+
],
1373+
"selected": "alphabetical",
1374+
},
1375+
]
1376+
12691377
context["create_account_card_preview_url"] = (
12701378
f"{settings.STATIC_URL}img/checker.png"
12711379
)

static/css/v3/components.css

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,5 @@
4242
@import "./markdown-card.css";
4343
@import "./user-profile.css";
4444
@import "./user-card.css";
45+
@import "./post-filter.css";
46+
@import "./library-filter.css";

static/css/v3/forms.css

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,15 @@
208208
border-color: var(--color-stroke-weak);
209209
}
210210

211+
.dropdown__trigger:has(:focus-visible) {
212+
outline: 2px solid var(--color-stroke-link-accent);
213+
background-color: var(--color-surface-mid);
214+
}
215+
216+
.dropdown--combo .dropdown__trigger:has(.combo__search-input:focus) {
217+
outline: none;
218+
}
219+
211220
.dropdown__trigger-text {
212221
flex: 1;
213222
min-width: 0;
@@ -257,21 +266,10 @@
257266
}
258267

259268
.dropdown__list {
260-
max-height: 320px;
269+
max-height: 280px;
261270
overflow-y: auto;
262-
}
263-
264-
.dropdown__list::-webkit-scrollbar {
265-
width: 4px;
266-
}
267-
268-
.dropdown__list::-webkit-scrollbar-track {
269-
background: transparent;
270-
}
271-
272-
.dropdown__list::-webkit-scrollbar-thumb {
273-
background-color: var(--color-stroke-mid, #0508162b);
274-
border-radius: 2px;
271+
scrollbar-width: thin;
272+
scrollbar-color: var(--color-icon-primary) var(--color-surface-strong);
275273
}
276274

277275
.dropdown__item {
@@ -339,6 +337,10 @@
339337
flex-shrink: 0;
340338
}
341339

340+
.combo__chevron-btn:focus-visible {
341+
outline: none;
342+
}
343+
342344
.combo__search-icon {
343345
width: 16px;
344346
height: 16px;

static/css/v3/library-filter.css

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/*
2+
Library Filter
3+
Horizontal row of filter dropdowns (desktop) with fullscreen modal (mobile).
4+
Mobile open/close is CSS-only via a hidden checkbox toggle.
5+
Depends on: primitives.css, semantics.css, forms.css
6+
*/
7+
8+
/* ── Desktop: hide trigger & nav, show fields inline ── */
9+
.library-filter__toggle {
10+
display: none;
11+
}
12+
13+
.library-filter__trigger {
14+
display: none;
15+
}
16+
17+
.library-filter__nav {
18+
display: none;
19+
}
20+
21+
.library-filter__fields {
22+
display: flex;
23+
flex-wrap: wrap;
24+
gap: var(--space-medium);
25+
}
26+
27+
/* ── Field width modifiers ─────────────── */
28+
29+
.library-filter__field {
30+
flex-shrink: 0;
31+
}
32+
33+
.library-filter__field-narrow {
34+
width: 104px;
35+
}
36+
37+
.library-filter__field-default {
38+
width: 160px;
39+
}
40+
41+
.library-filter__field-wide {
42+
width: 200px;
43+
}
44+
45+
/* ── Mobile: fullscreen modal ──────────── */
46+
47+
@media (max-width: 767px) {
48+
.library-filter__trigger {
49+
display: inline-flex;
50+
align-items: center;
51+
gap: var(--space-default);
52+
padding: var(--space-default) var(--space-large);
53+
background-color: var(--color-surface-weak);
54+
border: 1px solid var(--color-stroke-weak);
55+
border-radius: var(--border-radius-l);
56+
font-family: var(--font-sans);
57+
font-size: var(--font-size-small);
58+
font-weight: var(--font-weight-medium);
59+
color: var(--color-text-primary);
60+
text-decoration: none;
61+
cursor: pointer;
62+
}
63+
64+
.library-filter__trigger-icon {
65+
width: 16px;
66+
height: 16px;
67+
}
68+
69+
/* Modal: hidden by default, shown when checkbox is checked */
70+
.library-filter__modal {
71+
display: none;
72+
position: fixed;
73+
inset: 0;
74+
z-index: 10000;
75+
flex-direction: column;
76+
gap: var(--space-xl);
77+
background-color: var(--color-surface-page);
78+
overflow-y: auto;
79+
}
80+
81+
/* When the hidden checkbox is checked, show the modal and hide page content behind it */
82+
.library-filter__toggle:checked ~ .library-filter__modal {
83+
display: flex;
84+
}
85+
86+
.library-filter__toggle:checked ~ .library-filter__modal ~ * {
87+
display: none;
88+
}
89+
90+
/* Prevent background scroll while the modal is open */
91+
body:has(.library-filter__toggle:checked) {
92+
overflow: hidden;
93+
}
94+
95+
/* Nav bar with close button */
96+
.library-filter__nav {
97+
display: flex;
98+
justify-content: flex-end;
99+
align-items: center;
100+
gap: var(--space-medium);
101+
padding: var(--space-medium);
102+
height: 64px;
103+
box-sizing: border-box;
104+
}
105+
106+
.library-filter__close {
107+
display: inline-flex;
108+
align-items: center;
109+
gap: var(--space-default);
110+
height: 100%;
111+
padding: 0 var(--space-large);
112+
background-color: var(--color-surface-weak);
113+
border: 1px solid var(--color-stroke-weak);
114+
border-radius: var(--border-radius-xl);
115+
font-family: var(--font-sans);
116+
font-size: var(--font-size-small);
117+
font-weight: var(--font-weight-regular);
118+
color: var(--color-text-primary);
119+
text-decoration: none;
120+
cursor: pointer;
121+
}
122+
123+
.library-filter__close-icon {
124+
width: 16px;
125+
height: 16px;
126+
color: var(--color-icon-brand-accent);
127+
}
128+
129+
/* Fields stack vertically */
130+
.library-filter__fields {
131+
flex-direction: column;
132+
gap: var(--space-large);
133+
padding: 0 var(--space-default);
134+
}
135+
136+
.library-filter__field {
137+
width: 100%;
138+
}
139+
}

static/css/v3/post-filter.css

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
Post Filter
3+
Toggleable filter pill group. Each pill is a hidden radio + styled label.
4+
*/
5+
6+
.post-filter {
7+
display: flex;
8+
flex-wrap: wrap;
9+
gap: var(--space-s);
10+
}
11+
12+
/* ── Visually-hidden radio ──────────── */
13+
14+
.post-filter__input {
15+
position: absolute;
16+
width: 1px;
17+
height: 1px;
18+
padding: 0;
19+
margin: -1px;
20+
overflow: hidden;
21+
clip: rect(0, 0, 0, 0);
22+
white-space: nowrap;
23+
border: 0;
24+
}
25+
26+
/* ── Default pill ───────────────────── */
27+
28+
.post-filter__option {
29+
display: inline-flex;
30+
align-items: center;
31+
justify-content: center;
32+
padding: var(--space-s);
33+
font-family: var(--font-sans);
34+
font-size: var(--font-size-xs);
35+
font-weight: var(--font-weight-regular);
36+
color: var(--color-text-primary);
37+
background: var(--color-surface-weak);
38+
border: 1px solid var(--color-stroke-weak);
39+
border-radius: var(--border-radius-s);
40+
line-height: normal;
41+
cursor: pointer;
42+
transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
43+
}
44+
45+
/* ── Hover ──────────────────────────── */
46+
47+
.post-filter__option:hover {
48+
border-color: var(--color-stroke-mid);
49+
color: var(--color-text-link-accent);
50+
}
51+
52+
/* ── Selected ───────────────────────── */
53+
54+
.post-filter__input:checked + .post-filter__option {
55+
background: var(--color-surface-brand-accent-default);
56+
border-color: transparent;
57+
color: var(--color-text-on-accent);
58+
}
59+
60+
/* ── Focus-visible ──── */
61+
62+
.post-filter__input:focus-visible + .post-filter__option {
63+
outline: 2px solid var(--color-text-link-accent);
64+
outline-offset: -2px;
65+
}

0 commit comments

Comments
 (0)