diff --git a/core/views.py b/core/views.py index 488b42869..03c841091 100644 --- a/core/views.py +++ b/core/views.py @@ -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() @@ -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 diff --git a/static/css/v3/components.css b/static/css/v3/components.css index 244ab17b5..45e5661dd 100644 --- a/static/css/v3/components.css +++ b/static/css/v3/components.css @@ -46,3 +46,4 @@ @import "./post-card.css"; @import "./event-card.css"; @import "./badge-button.css"; +@import "./library-item.css"; diff --git a/static/css/v3/library-item.css b/static/css/v3/library-item.css new file mode 100644 index 000000000..08b3dcdcd --- /dev/null +++ b/static/css/v3/library-item.css @@ -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); + } +} diff --git a/templates/v3/examples/_v3_example_section.html b/templates/v3/examples/_v3_example_section.html index f41cc455f..b5acf9daf 100644 --- a/templates/v3/examples/_v3_example_section.html +++ b/templates/v3/examples/_v3_example_section.html @@ -223,6 +223,30 @@
{{ description }}
+ +