Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor research page #1602

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
135 changes: 135 additions & 0 deletions src/components/papers/PaperActions.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
---
interface Props {
bibtex?: string;
preprintUrl?: string;
doiUrl?: string;
hasAbstract: boolean;
toggleId: string;
}

const { bibtex, preprintUrl, doiUrl, hasAbstract, toggleId } = Astro.props;
---

<div class="flex gap-2">
{
bibtex && (
<button
class="paper-action"
aria-label="Show BibTeX"
data-bibtex={bibtex}
>
<span class="paper-action-text">CITE</span>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5"
viewBox="0 0 20 20"
fill="currentColor"
>
{' '}
{/* source https://heroicons.com v1.0.6, MIT licensed */}
<path d="M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z" />
<path d="M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z" />
</svg>
</button>
)
}
{
preprintUrl && (
<a
href={preprintUrl}
class="paper-action"
aria-label="Download preprint"
target="_blank"
>
{' '}
{/* source https://heroicons.com v1.0.6, MIT licensed */}
<span class="paper-action-text">PREPRINT</span>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fill-rule="evenodd"
d="M6 2a2 2 0 00-2 2v12a2 2 0 002 2h8a2 2 0 002-2V7.414A2 2 0 0015.414 6L12 2.586A2 2 0 0010.586 2H6zm5 6a1 1 0 10-2 0v3.586l-1.293-1.293a1 1 0 10-1.414 1.414l3 3a1 1 0 001.414 0l3-3a1 1 0 00-1.414-1.414L11 11.586V8z"
clip-rule="evenodd"
/>
</svg>
</a>
)
}
{
doiUrl && (
<a
href={doiUrl.startsWith('10.') ? `https://doi.org/${doiUrl}` : doiUrl}
class="paper-action"
aria-label="View publisher version"
target="_blank"
>
{' '}
{/* source https://heroicons.com v1.0.6, MIT licensed */}
<span class="paper-action-text">DOI</span>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2"
fill="none"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
/>
</svg>
</a>
)
}
{
hasAbstract && (
<label for={toggleId} class="paper-action" aria-label="Toggle abstract">
{' '}
{/* source https://heroicons.com v1.0.6, MIT licensed */}
<span class="paper-action-text">ABSTRACT</span>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6 transform transition-transform duration-200 peer-checked:rotate-180"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2"
fill="none"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M19 9l-7 7-7-7"
/>
</svg>
</label>
)
}
</div>

<style>
.paper-action {
@apply flex items-center gap-2 rounded-full p-2 text-gray-500 transition-all duration-300 hover:bg-gray-100 hover:text-gray-600;
}

.paper-action-text {
@apply w-0 overflow-hidden whitespace-nowrap text-sm font-bold opacity-0 transition-all duration-300;
}

/* Use standard CSS color: currentColor instead of Tailwind class */
.paper-action svg {
color: currentColor;
}

/* For devices that don't support hover */
@media (hover: none) {
.paper-action-text {
@apply ml-2 w-auto opacity-100;
}
}
</style>
98 changes: 98 additions & 0 deletions src/components/papers/PaperEntry.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
---
import type { Paper } from '../../types/papers';
import PaperActions from './PaperActions.astro';

interface Props {
paper: Paper;
index: number;
}

const { paper, index } = Astro.props;

function getToggleAbstractId(paper: Paper) {
return `toggle-abstract-${index}`;
}

function getVenueString(pubInfo: any) {
switch (pubInfo.type) {
case 'conference':
return `${pubInfo.conference}, ${pubInfo.location}`;
case 'journal':
return `${pubInfo.journal}${pubInfo.volume ? `, vol. ${pubInfo.volume}` : ''}${pubInfo.number ? `, no. ${pubInfo.number}` : ''}`;
case 'thesis':
return `${pubInfo.thesisType} thesis, ${pubInfo.institution}, ${pubInfo.location}`;
}
}
---

<article
class="shadow-sm group relative rounded-lg bg-white p-6 transition-all duration-200 hover:shadow-md"
>
<input type="checkbox" id={getToggleAbstractId(paper)} class="peer hidden" />

<div class="flex flex-col items-start justify-between sm:flex-row">
<div class="flex-1 space-y-2">
<h3 class="max-w-[650px] text-lg font-medium text-gray-900">
{paper.title}
</h3>
<p class="text-sm text-gray-700">
{
paper.authors.map((a, i) => (
<>
{a.orcidUrl ? (
<a
href={a.orcidUrl}
class="hover:text-blue-600 transition-colors duration-200"
target="_blank"
>
<span class="whitespace-nowrap">
{a.name}
{i < paper.authors.length - 1 ? ',' : ''}
</span>
</a>
) : (
<span class="whitespace-nowrap">
{a.name}
{i < paper.authors.length - 1 ? ',' : ''}
</span>
)}
{i < paper.authors.length - 1 ? ' ' : ''}
</>
))
}
</p>
<p class="text-sm text-gray-700">
{getVenueString(paper.publicationInfo)}
</p>
</div>

<div class="mt-4 flex w-full justify-end gap-2 sm:ml-4 sm:mt-0 sm:w-auto">
<PaperActions
bibtex={paper.bibtex}
preprintUrl={paper.preprintOrArchiveUrl}
doiUrl={paper.doiOrPublisherUrl}
hasAbstract={!!paper.abstract}
toggleId={getToggleAbstractId(paper)}
/>
</div>
</div>

{
paper.abstract && (
<div class="mt-4 max-h-0 overflow-hidden opacity-0 transition-all duration-300 peer-checked:max-h-[2000px] peer-checked:opacity-100">
<div class="mx-auto max-w-[600px] text-justify text-sm text-gray-700">
{paper.abstract.split('\n\n').map((paragraph, i) => (
<p class={i > 0 ? 'mt-4' : ''}>{paragraph}</p>
))}
</div>
</div>
)
}
</article>

<style>
/* Group hover effect */
article:hover :global(.paper-action-text) {
@apply ml-2 w-auto opacity-100;
}
</style>
18 changes: 18 additions & 0 deletions src/components/papers/YearSection.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
import type { Paper } from '../../types/papers';
import PaperEntry from './PaperEntry.astro';

interface Props {
year: string;
papers: Paper[];
}

const { year, papers } = Astro.props;
---

<section>
<h2 class="mb-6 text-2xl font-semibold">{year}</h2>
<div class="space-y-6">
{papers.map((paper, index) => <PaperEntry paper={paper} index={index} />)}
</div>
</section>
91 changes: 91 additions & 0 deletions src/pages/research/index.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
---
import Container from '../../components/layout/Container.astro';
import Divider from '../../components/layout/Divider.astro';
import PageHeader from '../../components/layout/PageHeader.astro';
import YearSection from '../../components/papers/YearSection.astro';
import Layout from '../../layouts/Layout.astro';
import { papers } from './papers';
import { papersSchema } from './papersSchema';

let validatedPapers;
try {
validatedPapers = papersSchema.parse(papers);
} catch (error) {
if (error instanceof z.ZodError) {
console.error('Papers validation failed:');
console.error(JSON.stringify(error.errors, null, 2));
throw new Error(
'Papers data validation failed. Check console for details.',
);
}
throw error;
}

const papersByYear = validatedPapers.reduce((acc, paper, index) => {
const year = paper.year;
if (!acc[year]) acc[year] = [];
paper.id = index;
acc[year].push(paper);
return acc;
}, {});

const sortedYears = Object.keys(papersByYear).sort((a, b) => b - a);
---

<Layout title="Research and Scientific Publications">
<PageHeader text="Research and Scientific Publications" />

<Container class="grid gap-8 px-12 pb-20 pt-8 md:grid-cols-2">
<div
class="flex flex-col justify-center gap-4 font-extralight leading-relaxed"
>
<p>
Nix started as a research project by Eelco Dolstra and his collaborators
at Utrecht University around 2003. Since then, scientists from multiple
institutions have published their work on repeatable computation and
reliable, secure software distribution.
</p>
<p>
This collection traces the continued exploration of theoretical
foundations and practical applications of the ideas underlying Nix and
its ecosystem.
</p>
</div>
<div class="flex flex-col items-center justify-center">
<img src="/images/explore/experiment.svg" class="max-h-96" />
</div>
</Container>

<Divider style="slope" mirrorX mirrorY />

<div class="bg-nix-blue py-16 text-white md:px-12 md:py-16">
<Container
class="mt-16 space-y-8 bg-nix-blue font-extralight leading-relaxed"
>
<h1 class="font-heading text-4xl font-bold leading-tight">
Scientific publications about Nix and associated projects
</h1>

<div class="space-y-12">
{
sortedYears.map((year) => (
<YearSection year={year} papers={papersByYear[year]} />
))
}
</div>
</Container>
</div>
</Layout>
<script>
document.querySelectorAll('.group').forEach((group) => {
group.addEventListener('mouseenter', () => {
console.log('Group hover entered');
const texts = group.querySelectorAll('.paper-action-text');
texts.forEach((text) => {
console.log('Text element:', text);
console.log('Text width:', text.offsetWidth);
console.log('Text opacity:', window.getComputedStyle(text).opacity);
});
});
});
</script>
Loading
Loading