| |
| import { |
| type HeroProps, |
| type Author, |
| normalizeAuthors, |
| getLinkIcon, |
| } from "./heroUtils"; |
|
|
| const { |
| title, |
| description, |
| authors = [], |
| affiliations = [], |
| affiliation, |
| published, |
| links = [], |
| } = Astro.props as HeroProps; |
|
|
| const normalizedAuthors: Author[] = normalizeAuthors(authors as any); |
|
|
| const authorAffiliationIndexSet = new Set<number>(); |
| for (const author of normalizedAuthors) { |
| const indices = Array.isArray(author.affiliationIndices) |
| ? author.affiliationIndices |
| : []; |
| for (const idx of indices) { |
| if (typeof idx === "number") authorAffiliationIndexSet.add(idx); |
| } |
| } |
| const shouldShowAffiliationSupers = authorAffiliationIndexSet.size > 1; |
|
|
| // Resolve affiliations: prefer array, fallback to legacy string |
| const resolvedAffiliations = |
| Array.isArray(affiliations) && affiliations.length > 0 |
| ? affiliations |
| : affiliation |
| ? [{ id: 1, name: affiliation }] |
| : []; |
| |
|
|
| <section class="hero-paper"> |
| <h1 class="hero-paper__title" set:html={title} /> |
| {description && <p class="hero-paper__desc">{description}</p>} |
| </section> |
|
|
| <script is:inline> |
| (() => { |
| const el = document.querySelector(".hero-paper__title"); |
| if (!el) return; |
| const len = (el.textContent || "").length; |
| if (len > 100) el.dataset.titleSize = "sm"; |
| else if (len > 60) el.dataset.titleSize = "md"; |
| })(); |
| </script> |
|
|
| <header class="hero-paper-meta" aria-label="Article meta information"> |
| <div class="hero-paper-meta__inner"> |
| {normalizedAuthors.length > 0 && ( |
| <span class="hero-paper-meta__segment"> |
| {normalizedAuthors.map((a, i) => ( |
| <> |
| {a.url ? <a href={a.url}>{a.name}</a> : <span>{a.name}</span>} |
| {shouldShowAffiliationSupers && |
| Array.isArray(a.affiliationIndices) && |
| a.affiliationIndices.length > 0 && ( |
| <sup>{a.affiliationIndices.join(",")}</sup> |
| )} |
| {i < normalizedAuthors.length - 1 && <span set:html=", " />} |
| </> |
| ))} |
| </span> |
| )} |
| {resolvedAffiliations.length > 0 && ( |
| <span class="hero-paper-meta__segment hero-paper-meta__segment--muted"> |
| {resolvedAffiliations.map((af, i) => ( |
| <> |
| {af.url ? ( |
| <a href={af.url} target="_blank" rel="noopener noreferrer">{af.name}</a> |
| ) : ( |
| <span>{af.name}</span> |
| )} |
| {i < resolvedAffiliations.length - 1 && <span set:html=", " />} |
| </> |
| ))} |
| </span> |
| )} |
| {published && ( |
| <span class="hero-paper-meta__segment hero-paper-meta__segment--muted"> |
| {published} |
| </span> |
| )} |
| </div> |
|
|
| {links && links.length > 0 && ( |
| <nav class="hero-paper-links" aria-label="External links"> |
| {links.map((link) => { |
| const iconSvg = getLinkIcon(link.label, link.icon); |
| return ( |
| <a href={link.url} class="hero-paper-link" target="_blank" rel="noopener noreferrer"> |
| {iconSvg && <span class="hero-paper-link__icon" set:html={iconSvg} />} |
| {link.label} |
| </a> |
| ); |
| })} |
| </nav> |
| )} |
| </header> |
|
|
| <style> |
| |
| .hero-paper { |
| width: 100%; |
| padding: 80px 16px 0; |
| text-align: center; |
| } |
| .hero-paper__title { |
| font-size: clamp(34px, 5vw, 54px); |
| font-weight: 800; |
| line-height: 1.12; |
| letter-spacing: -0.02em; |
| max-width: 860px; |
| margin: 0 auto 20px; |
| text-wrap: balance; |
| } |
| .hero-paper__title[data-title-size="md"] { |
| font-size: clamp(28px, 4vw, 44px); |
| } |
| .hero-paper__title[data-title-size="sm"] { |
| font-size: clamp(24px, 3.2vw, 38px); |
| } |
| .hero-paper__desc { |
| max-width: 680px; |
| margin: 0 auto; |
| font-size: 1.15em; |
| line-height: 1.6; |
| color: var( |
| } |
|
|
| |
| .hero-paper-meta { |
| padding: 24px 0 0; |
| font-size: 0.9rem; |
| text-align: center; |
| } |
| .hero-paper-meta__inner { |
| max-width: 860px; |
| margin: 0 auto; |
| padding: 0 var( |
| } |
| .hero-paper-meta__segment { |
| display: inline; |
| } |
| .hero-paper-meta__segment + .hero-paper-meta__segment::before { |
| content: " · "; |
| color: var( |
| font-weight: 400; |
| } |
| .hero-paper-meta__segment |
| color: var( |
| } |
| .hero-paper-meta__segment a { |
| color: var( |
| text-decoration: underline; |
| text-underline-offset: 2px; |
| text-decoration-thickness: 0.06em; |
| text-decoration-color: var( |
| transition: text-decoration-color 0.15s ease-in-out; |
| } |
| .hero-paper-meta__segment a:hover { |
| color: var( |
| text-decoration-color: var( |
| } |
|
|
| |
| .hero-paper-links { |
| display: flex; |
| justify-content: center; |
| flex-wrap: wrap; |
| gap: 8px; |
| margin-top: 20px; |
| padding: 0 var( |
| } |
| .hero-paper-link { |
| display: inline-flex; |
| align-items: center; |
| gap: 6px; |
| padding: 6px 16px; |
| font-size: 0.85em; |
| font-weight: 500; |
| color: var( |
| background: var( |
| border: 1px solid var( |
| border-radius: 20px; |
| text-decoration: none; |
| transition: border-color 0.15s, background 0.15s; |
| } |
| .hero-paper-link:hover { |
| border-color: var( |
| background: var( |
| } |
| .hero-paper-link__icon { |
| display: inline-flex; |
| align-items: center; |
| line-height: 0; |
| } |
| .hero-paper-link__icon :global(svg) { |
| width: 14px; |
| height: 14px; |
| } |
| </style> |
|
|