Skip to content
Merged
Show file tree
Hide file tree
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
79 changes: 79 additions & 0 deletions core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1252,6 +1252,7 @@ def get_context_data(self, **kwargs):
build_library_intro_context,
get_commit_data_by_release_for_library,
commit_data_to_stats_bars,
patch_commit_authors,
)

CODE_DEMO_BEAST = """int main()
Expand Down Expand Up @@ -1989,4 +1990,82 @@ def get_context_data(self, **kwargs):
else:
context["example_library_not_found"] = library_slug
context["example_library_slug"] = library_slug

demo_library_items = []
demo_libs_qs = (
Library.objects.filter(
slug__in=[
"accumulators",
"algorithm",
"align",
"any",
"array",
"asio",
"assign",
"atomic",
"beast",
"bimap",
"bind",
]
)
.prefetch_related("categories", "authors")
.order_by("name")
)
# Collect one author per demo library and patch CommitAuthor data
demo_authors = {}
for lib in demo_libs_qs:
author = (
lib.authors.exclude(email__startswith="deleted-")
.exclude(github_username="")
.first()
) or lib.authors.exclude(email__startswith="deleted-").first()
if author:
demo_authors[lib.pk] = author
patch_commit_authors(list(demo_authors.values()))

for lib in demo_libs_qs:
lv = (
LibraryVersion.objects.filter(version=latest, library=lib).first()
if latest
else None
)
cats = [
{"label": cat.name, "url": "#", "variant": "neutral"}
for cat in lib.categories.all()[:3]
]
author = demo_authors.get(lib.pk)
demo_library_items.append(
{
"library_name": lib.display_name_short,
"library_url": reverse(
"library-detail",
kwargs={
"version_slug": "latest",
"library_slug": lib.slug,
},
),
"description": lib.description or "",
"categories": cats,
"cpp_version": (
lv.get_cpp_standard_minimum_display()
if lv and lv.cpp_standard_minimum
else "C++03"
),
"author": {
"name": author.display_name if author else "Unknown",
"role": "Contributor",
"avatar_url": author.get_avatar_url() if author else "",
"badge_url": f"{badge_img}/badge-first-place.png",
},
"doc_url": reverse(
"library-detail",
kwargs={
"version_slug": "latest",
"library_slug": lib.slug,
},
),
}
)
context["demo_library_items"] = demo_library_items

return context
1 change: 1 addition & 0 deletions static/css/v3/components.css
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,4 @@
@import "./post-card.css";
@import "./event-card.css";
@import "./badge-button.css";
@import "./library-item.css";
301 changes: 301 additions & 0 deletions static/css/v3/library-item.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
/* ==========================================================================
Library Item — list row and card variants
========================================================================== */

/* Demo only: provides a contrasting background for the list variant in
Lookbook/examples so the item surface colours are visible. Remove when
the component is used on a real page with its own background. */
.v3-examples-section__example-box:has(.library-item-list) {
background: var(--color-surface-mid);
}

/* ---------- Containers ---------- */

.library-item-list {
list-style: none;
margin: 0;
padding: 0;
display: grid;
grid-template-columns: 160px 2fr 1fr 1fr 1fr auto;
column-gap: var(--space-xl);
max-width: 1408px;
width: 100%;
}

.library-item-card-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: var(--space-large);
}

@media (max-width: 1024px) {
.library-item-card-grid {
grid-template-columns: repeat(3, 1fr);
}
}

/* ---------- Shared element styles ---------- */

.library-item__name {
font-family: var(--font-display);
font-size: var(--font-size-base);
font-weight: var(--font-weight-medium);
line-height: var(--line-height-tight);
letter-spacing: var(--letter-spacing-display-regular);
color: var(--color-text-primary);
text-decoration: none;
}

a.library-item__name:hover {
text-decoration: underline;
}

.library-item p {
margin: 0;
padding: 0;
}

.library-item__description {
font-size: var(--font-size-small);
font-weight: var(--font-weight-regular);
line-height: var(--line-height-default);
letter-spacing: var(--letter-spacing-tight);
color: var(--color-text-secondary);
}

.library-item .btn-icon-library {
padding: 0;
border: none;
background: none;
width: var(--space-large);
}

.library-item .btn-icon-library .btn-icon svg {
width: 100%;
height: 100%;
}

/* Version tag visibility — desktop shows the tag inside .library-item__actions,
mobile/card shows the tag inline with category tags. */
.library-item__cpp-version {
display: none;
}

.library-item__cpp-version--desktop {
display: inline-flex;
width: fit-content;
justify-self: center;
}

/* ==========================================================================
List variant
========================================================================== */

/* ---------- Desktop ---------- */

.library-item--list {
display: grid;
grid-column: 1 / -1;
grid-template-columns: subgrid;
align-items: start;
row-gap: var(--space-large);
column-gap: var(--space-large);
padding: var(--space-large);
padding-top: var(--space-default);
background: var(--color-surface-weak);
border-bottom: 1px solid var(--color-stroke-weak);
}

.library-item--list .library-item__header {
display: contents;
}

.library-item--list .library-item__tags {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-self: start;
gap: var(--space-s);
}

.library-item--list .library-item__contributor {
display: flex;
align-items: start;
gap: var(--space-default);
}

.library-item--list .library-item__actions {
display: contents;
}

/* Right-align the doc button with extra spacing from version tag */
.library-item--list .library-item__actions > .btn-icon-library {
justify-self: end;
margin-left: var(--space-xl);
}

/* ---------- Tablet (≤ 1024px) ---------- */

@media (max-width: 1024px) {
.library-item-list {
grid-template-columns: auto 2fr 1fr 1fr min-content auto;
/* Figma specifies --space-large here, but long descriptions cause overflow
at tablet widths. Using --space-default until content constraints are
defined. */
column-gap: var(--space-default);
}

.library-item--list .user-profile {
align-items: normal;
}

.library-item--list .library-item__actions > .btn-icon-library {
margin-left: var(--space-default);
}
}

/* ---------- Mobile (≤ 767px) — switches to card-like layout ---------- */

@media (max-width: 767px) {
.library-item-list {
display: flex;
flex-direction: column;
}

/* Container — stacked cards with shared borders */
.library-item--list {
display: grid;
grid-template-columns: 1fr auto;
row-gap: var(--space-medium);
border-bottom: 1px solid var(--color-stroke-weak);
border-radius: 0;
background: var(--color-surface-weak);
padding: var(--space-large);
}

.library-item--list:first-child {
border-radius: var(--border-radius-xl) var(--border-radius-xl) 0 0;
}

.library-item--list:last-child {
border-radius: 0 0 var(--border-radius-xl) var(--border-radius-xl);
border-bottom: none;
}

.library-item--list:only-child {
border-radius: var(--border-radius-xl);
}

/* Header — name + description, left column */
.library-item--list .library-item__header {
grid-column: 1;
grid-row: 1;
display: flex;
flex-direction: column;
gap: var(--space-card);
}

/* Actions — doc icon only, top-right */
.library-item--list .library-item__actions {
grid-column: 2;
grid-row: 1;
min-width: unset;
}

/* Version tag swap — hide desktop, show mobile (inline with tags) */
.library-item--list .library-item__cpp-version--desktop {
display: none;
}

.library-item--list .library-item__cpp-version {
display: inline-flex;
}

/* Tags — category tags + C++ version, full width */
.library-item--list .library-item__tags {
grid-column: 1 / -1;
grid-row: 2;
}

/* Contributor — full width, bottom */
.library-item--list .library-item__contributor {
grid-column: 1 / -1;
grid-row: 3;
margin-top: auto;
}
}

/* ==========================================================================
Card variant
========================================================================== */

/* ---------- Desktop ---------- */

.library-item--card {
display: grid;
grid-template-columns: 1fr auto;
column-gap: var(--space-large);
width: 100%;
border-radius: var(--border-radius-xl);
border: 1px solid var(--color-stroke-mid);
background: var(--color-surface-weak);
padding: var(--space-large);
}

.library-item--card .library-item__header {
grid-column: 1;
grid-row: 1;
display: flex;
flex-direction: column;
gap: var(--space-large);
}

.library-item--card .library-item__actions {
grid-column: 2;
grid-row: 1;
display: flex;
align-items: start;
justify-content: end;
}

.library-item--card .library-item__cpp-version--desktop {
display: none;
}

.library-item--card .library-item__cpp-version {
display: inline-flex;
}

.library-item--card .library-item__tags {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--space-s);
}

.library-item--card .library-item__contributor {
grid-column: 1 / -1;
grid-row: 2;
display: flex;
align-items: center;
gap: var(--space-default);
margin-top: auto;
padding-top: var(--space-large);
}

/* ---------- Mobile (≤ 767px) ---------- */

@media (max-width: 767px) {
.library-item-card-grid {
grid-template-columns: 1fr;
gap: var(--space-medium);
}

.library-item--card .library-item__header {
gap: var(--space-card);
}

.library-item--card .library-item__contributor {
padding-top: var(--space-card);
}
}
Loading
Loading