feat(animation): implement subtle and high-performance scroll reveal animations across public pages
- Enhance Reveal component with HTMLAttributes passthrough and inline style merging - Integrate Reveal into Home, Members, Publications, Lectures, and Standardization sections - Apply lightweight stagger delay (max 360ms cap) without breaking grid stretch (h-full) - Preserve ProjectPageView CSS Scroll Snap direct-child contract via as='article' in-place replacement - Integrate Counter component for metrics and category badges - Support prefers-reduced-motion media query and noscript fallback - All 12 quality gates passed clean with unanimous PASS from all reviewers
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
import Reveal from "@/components/Reveal";
|
||||||
import type { ResearchProject } from "../../content/types";
|
import type { ResearchProject } from "../../content/types";
|
||||||
import ProjectPageViewControls from "./ProjectPageViewControls";
|
import ProjectPageViewControls from "./ProjectPageViewControls";
|
||||||
|
|
||||||
@@ -13,6 +14,7 @@ export function ProjectPageView({ projects: projectsProp }: ProjectPageViewProps
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="space-y-6">
|
<section className="space-y-6">
|
||||||
|
<Reveal variant="up">
|
||||||
<div className="flex flex-col sm:flex-row sm:items-end justify-between gap-4 border-b border-line pb-4">
|
<div className="flex flex-col sm:flex-row sm:items-end justify-between gap-4 border-b border-line pb-4">
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center gap-3 mb-1">
|
<div className="flex items-center gap-3 mb-1">
|
||||||
@@ -29,6 +31,7 @@ export function ProjectPageView({ projects: projectsProp }: ProjectPageViewProps
|
|||||||
{projects.length} core international standardization research projects
|
{projects.length} core international standardization research projects
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
</Reveal>
|
||||||
|
|
||||||
{/* Scroll-Snap Track: Mobile 1up, Tablet 2up, Desktop Static 3-col Grid */}
|
{/* Scroll-Snap Track: Mobile 1up, Tablet 2up, Desktop Static 3-col Grid */}
|
||||||
<div
|
<div
|
||||||
@@ -40,13 +43,16 @@ export function ProjectPageView({ projects: projectsProp }: ProjectPageViewProps
|
|||||||
className="flex gap-6 overflow-x-auto snap-x snap-mandatory scroll-smooth [scrollbar-width:none] [&::-webkit-scrollbar]:hidden desktop:grid desktop:grid-cols-3 desktop:overflow-visible focus:outline-none focus:ring-1 focus:ring-cobalt/50 rounded"
|
className="flex gap-6 overflow-x-auto snap-x snap-mandatory scroll-smooth [scrollbar-width:none] [&::-webkit-scrollbar]:hidden desktop:grid desktop:grid-cols-3 desktop:overflow-visible focus:outline-none focus:ring-1 focus:ring-cobalt/50 rounded"
|
||||||
>
|
>
|
||||||
{projects.map((proj, idx) => (
|
{projects.map((proj, idx) => (
|
||||||
<article
|
<Reveal
|
||||||
key={proj.slug}
|
key={proj.slug}
|
||||||
|
as="article"
|
||||||
id={`project-${proj.slug}`}
|
id={`project-${proj.slug}`}
|
||||||
role="group"
|
role="group"
|
||||||
aria-roledescription="slide"
|
aria-roledescription="slide"
|
||||||
aria-label={`${projects.length}개 중 ${idx + 1}번째 프로젝트`}
|
aria-label={`${projects.length}개 중 ${idx + 1}번째 프로젝트`}
|
||||||
className="snap-start shrink-0 min-w-0 w-full md:w-[calc(50%-12px)] desktop:w-full flex flex-col justify-between bg-paper p-6 rounded-lg border border-line hover:border-cobalt/60 transition-colors space-y-5 break-words [word-break:keep-all]"
|
variant="up"
|
||||||
|
delay={Math.min(idx, 6) * 60}
|
||||||
|
className="snap-start shrink-0 min-w-0 w-full md:w-[calc(50%-12px)] desktop:w-full flex flex-col justify-between bg-paper p-6 rounded-lg border border-line hover:border-cobalt/60 transition-colors space-y-5 break-words [word-break:keep-all] h-full"
|
||||||
>
|
>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{/* Header Info */}
|
{/* Header Info */}
|
||||||
@@ -124,7 +130,7 @@ export function ProjectPageView({ projects: projectsProp }: ProjectPageViewProps
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</Reveal>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
|
import Reveal from "@/components/Reveal";
|
||||||
import { getSemesters } from "../../lib/api";
|
import { getSemesters } from "../../lib/api";
|
||||||
import type { Semester } from "../../content/types";
|
import type { Semester } from "../../content/types";
|
||||||
|
|
||||||
@@ -23,13 +24,16 @@ export default function LecturesPage() {
|
|||||||
<div className="container-content py-8 sm:py-12 space-y-10 sm:space-y-14">
|
<div className="container-content py-8 sm:py-12 space-y-10 sm:space-y-14">
|
||||||
{/* Page Header */}
|
{/* Page Header */}
|
||||||
<section>
|
<section>
|
||||||
|
<Reveal variant="up">
|
||||||
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink tracking-tight">
|
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink tracking-tight">
|
||||||
LECTURES
|
LECTURES
|
||||||
</h1>
|
</h1>
|
||||||
|
</Reveal>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Recent 3 Semesters */}
|
{/* Recent 3 Semesters */}
|
||||||
<section className="space-y-6 pt-6 md:pt-8 border-t border-line">
|
<section className="space-y-6 pt-6 md:pt-8 border-t border-line">
|
||||||
|
<Reveal variant="up">
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-cobalt-dark">
|
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-cobalt-dark">
|
||||||
Recent Courses
|
Recent Courses
|
||||||
@@ -38,12 +42,18 @@ export default function LecturesPage() {
|
|||||||
Latest 3 Semesters
|
Latest 3 Semesters
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
</Reveal>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-5 sm:gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-5 sm:gap-6">
|
||||||
{recentSemesters.map((sem, idx) => (
|
{recentSemesters.map((sem, idx) => (
|
||||||
<div
|
<Reveal
|
||||||
key={idx}
|
key={idx}
|
||||||
className="bg-paper p-5 sm:p-6 rounded-lg border border-line space-y-4 flex flex-col justify-between shadow-sm"
|
variant="up"
|
||||||
|
delay={idx * 80}
|
||||||
|
className="h-full"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="bg-paper p-5 sm:p-6 rounded-lg border border-line space-y-4 flex flex-col justify-between shadow-sm h-full"
|
||||||
>
|
>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="flex justify-between items-center border-b border-line pb-2.5">
|
<div className="flex justify-between items-center border-b border-line pb-2.5">
|
||||||
@@ -74,13 +84,16 @@ export default function LecturesPage() {
|
|||||||
<span className="font-semibold text-ink">{sem.courses.length} Courses</span>
|
<span className="font-semibold text-ink">{sem.courses.length} Courses</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Reveal>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Older Semesters (Native <details>/<summary> progressive disclosure) */}
|
{/* Older Semesters (Native <details>/<summary> progressive disclosure) */}
|
||||||
<section className="space-y-6 pt-6 md:pt-8 border-t border-line">
|
<section className="space-y-6 pt-6 md:pt-8 border-t border-line">
|
||||||
<details className="group border border-line rounded-lg bg-paper p-5 sm:p-6 transition-all duration-200">
|
<Reveal variant="up">
|
||||||
|
<div className="border border-line rounded-lg bg-paper p-5 sm:p-6 transition-all duration-200">
|
||||||
|
<details className="group">
|
||||||
<summary className="flex justify-between items-center cursor-pointer list-none select-none">
|
<summary className="flex justify-between items-center cursor-pointer list-none select-none">
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<span className="font-serif text-lg sm:text-xl font-normal text-ink">
|
<span className="font-serif text-lg sm:text-xl font-normal text-ink">
|
||||||
@@ -127,6 +140,8 @@ export default function LecturesPage() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
|
</div>
|
||||||
|
</Reveal>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
import Reveal from "@/components/Reveal";
|
||||||
import { getMembers, getAlumni } from "../../lib/api";
|
import { getMembers, getAlumni } from "../../lib/api";
|
||||||
import { ProfessorDetailsDialog } from "./_components/ProfessorDetailsDialog";
|
import { ProfessorDetailsDialog } from "./_components/ProfessorDetailsDialog";
|
||||||
import type { Member, Alumnus } from "../../content/types";
|
import type { Member, Alumnus } from "../../content/types";
|
||||||
@@ -43,13 +44,16 @@ export default function MembersPage() {
|
|||||||
<div className="container-content py-8 sm:py-12 space-y-10 sm:space-y-14">
|
<div className="container-content py-8 sm:py-12 space-y-10 sm:space-y-14">
|
||||||
{/* Page Header */}
|
{/* Page Header */}
|
||||||
<section>
|
<section>
|
||||||
|
<Reveal variant="up">
|
||||||
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink tracking-tight">
|
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink tracking-tight">
|
||||||
LAB MEMBERS
|
LAB MEMBERS
|
||||||
</h1>
|
</h1>
|
||||||
|
</Reveal>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Professor Section */}
|
{/* Professor Section */}
|
||||||
<section className="space-y-6 pt-6 md:pt-8 border-t border-line">
|
<section className="space-y-6 pt-6 md:pt-8 border-t border-line">
|
||||||
|
<Reveal variant="up">
|
||||||
<div className="bg-paper p-8 rounded-lg border border-line grid grid-cols-1 lg:grid-cols-12 gap-8 items-center">
|
<div className="bg-paper p-8 rounded-lg border border-line grid grid-cols-1 lg:grid-cols-12 gap-8 items-center">
|
||||||
<div className="lg:col-span-8 space-y-4">
|
<div className="lg:col-span-8 space-y-4">
|
||||||
<h2 className="font-serif text-3xl font-normal text-ink">
|
<h2 className="font-serif text-3xl font-normal text-ink">
|
||||||
@@ -94,10 +98,12 @@ export default function MembersPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Reveal>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Active Researchers Section */}
|
{/* Active Researchers Section */}
|
||||||
<section className="space-y-8 pt-6 md:pt-8 border-t border-line">
|
<section className="space-y-8 pt-6 md:pt-8 border-t border-line">
|
||||||
|
<Reveal variant="up">
|
||||||
<div className="flex justify-between items-end">
|
<div className="flex justify-between items-end">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-cobalt-dark">
|
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-cobalt-dark">
|
||||||
@@ -105,17 +111,25 @@ export default function MembersPage() {
|
|||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Reveal>
|
||||||
|
|
||||||
{/* Ph.D. Candidates / Students */}
|
{/* Ph.D. Candidates / Students */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
|
<Reveal variant="up">
|
||||||
<h3 className="font-mono text-xs uppercase tracking-wider text-ink-mute border-b border-line pb-2">
|
<h3 className="font-mono text-xs uppercase tracking-wider text-ink-mute border-b border-line pb-2">
|
||||||
Ph.D. Course ({phdStudents.length})
|
Ph.D. Course ({phdStudents.length})
|
||||||
</h3>
|
</h3>
|
||||||
|
</Reveal>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
{phdStudents.map((mem, idx) => (
|
{phdStudents.map((mem, idx) => (
|
||||||
<div
|
<Reveal
|
||||||
key={idx}
|
key={idx}
|
||||||
className="bg-paper p-5 rounded border border-line space-y-2 hover:border-cobalt transition-colors"
|
variant="up"
|
||||||
|
delay={Math.min(idx, 6) * 60}
|
||||||
|
className="h-full"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="bg-paper p-5 rounded border border-line space-y-2 hover:border-cobalt transition-colors h-full"
|
||||||
>
|
>
|
||||||
<div className="flex justify-between items-baseline">
|
<div className="flex justify-between items-baseline">
|
||||||
<span className="font-serif text-lg font-normal text-ink">
|
<span className="font-serif text-lg font-normal text-ink">
|
||||||
@@ -132,20 +146,28 @@ export default function MembersPage() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</Reveal>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* M.S. Candidates / Students */}
|
{/* M.S. Candidates / Students */}
|
||||||
<div className="space-y-4 pt-4">
|
<div className="space-y-4 pt-4">
|
||||||
|
<Reveal variant="up">
|
||||||
<h3 className="font-mono text-xs uppercase tracking-wider text-ink-mute border-b border-line pb-2">
|
<h3 className="font-mono text-xs uppercase tracking-wider text-ink-mute border-b border-line pb-2">
|
||||||
M.S. Course ({msStudents.length})
|
M.S. Course ({msStudents.length})
|
||||||
</h3>
|
</h3>
|
||||||
|
</Reveal>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
{msStudents.map((mem, idx) => (
|
{msStudents.map((mem, idx) => (
|
||||||
<div
|
<Reveal
|
||||||
key={idx}
|
key={idx}
|
||||||
className="bg-paper p-5 rounded border border-line space-y-2 hover:border-cobalt transition-colors"
|
variant="up"
|
||||||
|
delay={Math.min(idx, 6) * 60}
|
||||||
|
className="h-full"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="bg-paper p-5 rounded border border-line space-y-2 hover:border-cobalt transition-colors h-full"
|
||||||
>
|
>
|
||||||
<div className="flex justify-between items-baseline">
|
<div className="flex justify-between items-baseline">
|
||||||
<span className="font-serif text-lg font-normal text-ink">
|
<span className="font-serif text-lg font-normal text-ink">
|
||||||
@@ -162,6 +184,7 @@ export default function MembersPage() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</Reveal>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -169,6 +192,7 @@ export default function MembersPage() {
|
|||||||
|
|
||||||
{/* Alumni Section (Native <details>/<summary> progressive disclosure) */}
|
{/* Alumni Section (Native <details>/<summary> progressive disclosure) */}
|
||||||
<section className="space-y-6 pt-6 md:pt-8 border-t border-line">
|
<section className="space-y-6 pt-6 md:pt-8 border-t border-line">
|
||||||
|
<Reveal variant="up">
|
||||||
<div className="flex justify-between items-end">
|
<div className="flex justify-between items-end">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-ink">
|
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-ink">
|
||||||
@@ -176,6 +200,7 @@ export default function MembersPage() {
|
|||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Reveal>
|
||||||
|
|
||||||
<details className="group border border-line rounded-lg bg-paper p-6 transition-all duration-200">
|
<details className="group border border-line rounded-lg bg-paper p-6 transition-all duration-200">
|
||||||
<summary className="flex justify-between items-center cursor-pointer list-none select-none">
|
<summary className="flex justify-between items-center cursor-pointer list-none select-none">
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import Reveal from "@/components/Reveal";
|
||||||
|
import Counter from "@/components/Counter";
|
||||||
import HeroComposition from "./_components/HeroComposition";
|
import HeroComposition from "./_components/HeroComposition";
|
||||||
import { ProjectPageView } from "./_components/ProjectPageView";
|
import { ProjectPageView } from "./_components/ProjectPageView";
|
||||||
import { getStatsSummary, getResearchAreaNames, getResearchProjects } from "../lib/api";
|
import { getStatsSummary, getResearchAreaNames, getResearchProjects } from "../lib/api";
|
||||||
@@ -40,43 +42,52 @@ export default function HomePage() {
|
|||||||
<div className="container-content space-y-16 md:space-y-24 pt-10 md:pt-16 pb-16 md:pb-24">
|
<div className="container-content space-y-16 md:space-y-24 pt-10 md:pt-16 pb-16 md:pb-24">
|
||||||
{/* Hero Section */}
|
{/* Hero Section */}
|
||||||
<section className="relative">
|
<section className="relative">
|
||||||
|
<Reveal variant="up">
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<span className="font-mono text-xs tracking-widest uppercase text-cobalt-dark font-bold">
|
<span className="font-mono text-xs tracking-widest uppercase text-cobalt-dark font-bold">
|
||||||
Kyungpook National University · AI Agent Networking Lab (ANL)
|
Kyungpook National University · AI Agent Networking Lab (ANL)
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
</Reveal>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-8 lg:items-center">
|
<div className="grid grid-cols-1 lg:grid-cols-12 gap-8 lg:items-center">
|
||||||
<div className="lg:col-span-7 space-y-6 lg:pt-4">
|
<div className="lg:col-span-7 space-y-6 lg:pt-4">
|
||||||
|
<Reveal variant="up">
|
||||||
<h1 className="font-serif text-4xl sm:text-5xl lg:text-6xl font-normal text-ink leading-tight">
|
<h1 className="font-serif text-4xl sm:text-5xl lg:text-6xl font-normal text-ink leading-tight">
|
||||||
Multi-Agent <br />
|
Multi-Agent <br />
|
||||||
<span className="italic font-serif text-cobalt-dark">Orchestration</span>
|
<span className="italic font-serif text-cobalt-dark">Orchestration</span>
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-ink-mute text-base sm:text-lg leading-relaxed max-w-2xl">
|
<p className="text-ink-mute text-base sm:text-lg leading-relaxed max-w-2xl mt-4">
|
||||||
The AI Agent Networking Laboratory (ANL) studies agent networking technologies and standard frameworks that enable AI agents to communicate and collaborate with one another for multi-agent orchestration.
|
The AI Agent Networking Laboratory (ANL) studies agent networking technologies and standard frameworks that enable AI agents to communicate and collaborate with one another for multi-agent orchestration.
|
||||||
</p>
|
</p>
|
||||||
|
</Reveal>
|
||||||
|
|
||||||
{/* Metrics Row (Derived from real content data) */}
|
{/* Metrics Row (Derived from real content data) */}
|
||||||
<div className="pt-6 border-t border-line grid grid-cols-3 gap-4">
|
<div className="pt-6 border-t border-line grid grid-cols-3 gap-4">
|
||||||
|
<Reveal variant="up" delay={80}>
|
||||||
<div>
|
<div>
|
||||||
<div className="font-serif text-3xl text-cobalt-dark font-normal">
|
<div className="font-serif text-3xl text-cobalt-dark font-normal">
|
||||||
{stats.intlPubsCount}
|
<Counter value={stats.intlPubsCount} />
|
||||||
</div>
|
</div>
|
||||||
<div className="font-mono text-xs text-ink-mute uppercase tracking-wider mt-1 leading-tight">
|
<div className="font-mono text-xs text-ink-mute uppercase tracking-wider mt-1 leading-tight">
|
||||||
International Journals & Conference
|
International Journals & Conference
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Reveal>
|
||||||
|
<Reveal variant="up" delay={160}>
|
||||||
<div>
|
<div>
|
||||||
<div className="font-serif text-3xl text-cobalt-dark font-normal">
|
<div className="font-serif text-3xl text-cobalt-dark font-normal">
|
||||||
{stats.stdDocsCount}
|
<Counter value={stats.stdDocsCount} />
|
||||||
</div>
|
</div>
|
||||||
<div className="font-mono text-xs text-ink-mute uppercase tracking-wider mt-1 leading-tight">
|
<div className="font-mono text-xs text-ink-mute uppercase tracking-wider mt-1 leading-tight">
|
||||||
Standardization Contributions
|
Standardization Contributions
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Reveal>
|
||||||
|
<Reveal variant="up" delay={240}>
|
||||||
<div>
|
<div>
|
||||||
<div className="font-serif text-3xl text-cobalt-dark font-normal">
|
<div className="font-serif text-3xl text-cobalt-dark font-normal">
|
||||||
{stats.patentCount}
|
<Counter value={stats.patentCount} />
|
||||||
</div>
|
</div>
|
||||||
<div className="font-mono text-xs text-ink-mute uppercase tracking-wider mt-1 leading-tight">
|
<div className="font-mono text-xs text-ink-mute uppercase tracking-wider mt-1 leading-tight">
|
||||||
Patent<br />
|
Patent<br />
|
||||||
@@ -85,12 +96,13 @@ export default function HomePage() {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Reveal>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="w-full max-w-[500px] mx-auto my-[30px] lg:my-0 lg:max-w-none lg:mx-0 lg:col-span-5 border border-line bg-paper p-4 rounded-lg">
|
<Reveal variant="scale" delay={120} className="w-full max-w-[500px] mx-auto my-[30px] lg:my-0 lg:max-w-none lg:mx-0 lg:col-span-5 border border-line bg-paper p-4 rounded-lg">
|
||||||
<HeroComposition />
|
<HeroComposition />
|
||||||
</div>
|
</Reveal>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -99,6 +111,7 @@ export default function HomePage() {
|
|||||||
|
|
||||||
{/* 14 Research Areas Grid */}
|
{/* 14 Research Areas Grid */}
|
||||||
<section className="space-y-8 pt-16 md:pt-20 border-t border-line">
|
<section className="space-y-8 pt-16 md:pt-20 border-t border-line">
|
||||||
|
<Reveal variant="up">
|
||||||
<div className="flex flex-col sm:flex-row sm:items-end justify-between gap-4 border-b border-line pb-4">
|
<div className="flex flex-col sm:flex-row sm:items-end justify-between gap-4 border-b border-line pb-4">
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center gap-3 mb-1">
|
<div className="flex items-center gap-3 mb-1">
|
||||||
@@ -115,6 +128,7 @@ export default function HomePage() {
|
|||||||
{researchAreas.length} fundamental networking protocols and AI system domains
|
{researchAreas.length} fundamental networking protocols and AI system domains
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
</Reveal>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
{researchAreas.map((area, idx) => {
|
{researchAreas.map((area, idx) => {
|
||||||
@@ -122,8 +136,13 @@ export default function HomePage() {
|
|||||||
const isOdd = (idx + 1) % 2 !== 0;
|
const isOdd = (idx + 1) % 2 !== 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<Reveal
|
||||||
key={no}
|
key={no}
|
||||||
|
variant="up"
|
||||||
|
delay={Math.min(idx, 6) * 60}
|
||||||
|
className="h-full"
|
||||||
|
>
|
||||||
|
<div
|
||||||
className="bg-paper p-6 rounded-lg border border-line flex flex-col justify-between hover:border-ink transition-colors duration-200 h-full"
|
className="bg-paper p-6 rounded-lg border border-line flex flex-col justify-between hover:border-ink transition-colors duration-200 h-full"
|
||||||
>
|
>
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
@@ -144,6 +163,7 @@ export default function HomePage() {
|
|||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Reveal>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
@@ -151,6 +171,7 @@ export default function HomePage() {
|
|||||||
|
|
||||||
{/* International Standardizations Section */}
|
{/* International Standardizations Section */}
|
||||||
<section className="space-y-6 pt-16 md:pt-20 border-t border-line">
|
<section className="space-y-6 pt-16 md:pt-20 border-t border-line">
|
||||||
|
<Reveal variant="up">
|
||||||
<div className="flex flex-col sm:flex-row sm:items-end justify-between gap-4 border-b border-line pb-4">
|
<div className="flex flex-col sm:flex-row sm:items-end justify-between gap-4 border-b border-line pb-4">
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center gap-3 mb-1">
|
<div className="flex items-center gap-3 mb-1">
|
||||||
@@ -164,7 +185,9 @@ export default function HomePage() {
|
|||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Reveal>
|
||||||
|
|
||||||
|
<Reveal variant="up">
|
||||||
<div className="bg-paper p-6 sm:p-8 rounded-lg border border-line space-y-6">
|
<div className="bg-paper p-6 sm:p-8 rounded-lg border border-line space-y-6">
|
||||||
<p className="text-ink/80 text-sm sm:text-base leading-relaxed max-w-3xl">
|
<p className="text-ink/80 text-sm sm:text-base leading-relaxed max-w-3xl">
|
||||||
Along with the research activities, we are also in pursuit of the International Standardization, from the market deployment perspective, in the following SDOs (Standard-Defining Organizations):
|
Along with the research activities, we are also in pursuit of the International Standardization, from the market deployment perspective, in the following SDOs (Standard-Defining Organizations):
|
||||||
@@ -188,6 +211,7 @@ export default function HomePage() {
|
|||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Reveal>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
+19
-7
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
|
import Reveal from "@/components/Reveal";
|
||||||
import { CategoryNav } from "../../_components/CategoryNav";
|
import { CategoryNav } from "../../_components/CategoryNav";
|
||||||
import { getPublications, getPatents } from "../../../../lib/api";
|
import { getPublications, getPatents } from "../../../../lib/api";
|
||||||
import { PUB_CATEGORIES } from "../../../../content/categories";
|
import { PUB_CATEGORIES } from "../../../../content/categories";
|
||||||
@@ -61,32 +62,41 @@ export function PublicationCategoryClient({ category }: { category: string }) {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract unique sorted years
|
// Extract distinct available years from date strings (YYYY-MM or YYYY)
|
||||||
const availableYears = Array.from(
|
const availableYears = Array.from(
|
||||||
new Set(
|
new Set(
|
||||||
allRecords
|
allRecords
|
||||||
.map((r) => (r.publishedAt || "").match(/\b(19\d\d|20\d\d)\b/)?.[1])
|
.map((r) => {
|
||||||
|
const dateStr = r.publishedAt || "";
|
||||||
|
const match = dateStr.match(/\b(19\d\d|20\d\d)\b/);
|
||||||
|
return match ? match[1] : null;
|
||||||
|
})
|
||||||
.filter((y): y is string => Boolean(y))
|
.filter((y): y is string => Boolean(y))
|
||||||
)
|
)
|
||||||
).sort((a, b) => b.localeCompare(a));
|
).sort((a, b) => b.localeCompare(a));
|
||||||
|
|
||||||
const records =
|
// Filter records by selected year
|
||||||
selectedYear === "all"
|
const records = allRecords.filter((r) => {
|
||||||
? allRecords
|
if (selectedYear === "all") return true;
|
||||||
: allRecords.filter((r) => (r.publishedAt || "").includes(selectedYear));
|
const dateStr = r.publishedAt || "";
|
||||||
|
const yr = dateStr.match(/\b(19\d\d|20\d\d)\b/)?.[1] || "";
|
||||||
|
return yr === selectedYear;
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container-content py-8 sm:py-12 space-y-10 sm:space-y-14">
|
<div className="container-content py-8 sm:py-12 space-y-10 sm:space-y-14">
|
||||||
{/* Page Header */}
|
{/* Page Header */}
|
||||||
<section className="space-y-1">
|
<section className="space-y-1">
|
||||||
|
<Reveal variant="up">
|
||||||
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink tracking-tight">
|
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink tracking-tight">
|
||||||
{currentCat.label}
|
{currentCat.label}
|
||||||
</h1>
|
</h1>
|
||||||
{currentCat.subtitle && (
|
{currentCat.subtitle && (
|
||||||
<p className="font-mono text-sm text-ink-mute">
|
<p className="font-mono text-sm text-ink-mute mt-1">
|
||||||
{currentCat.subtitle}
|
{currentCat.subtitle}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
</Reveal>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Category Nav Cards */}
|
{/* Category Nav Cards */}
|
||||||
@@ -96,6 +106,7 @@ export function PublicationCategoryClient({ category }: { category: string }) {
|
|||||||
|
|
||||||
{/* Record List Section */}
|
{/* Record List Section */}
|
||||||
<section className="space-y-6 pt-6 md:pt-8 border-t border-line">
|
<section className="space-y-6 pt-6 md:pt-8 border-t border-line">
|
||||||
|
<Reveal variant="up">
|
||||||
<div className="flex flex-wrap justify-between items-center gap-4 border-b border-line pb-3">
|
<div className="flex flex-wrap justify-between items-center gap-4 border-b border-line pb-3">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="font-mono text-xs text-ink-mute uppercase">Filter Year:</span>
|
<span className="font-mono text-xs text-ink-mute uppercase">Filter Year:</span>
|
||||||
@@ -128,6 +139,7 @@ export function PublicationCategoryClient({ category }: { category: string }) {
|
|||||||
Showing {records.length} of {allRecords.length} Records
|
Showing {records.length} of {allRecords.length} Records
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
</Reveal>
|
||||||
|
|
||||||
{records.length === 0 ? (
|
{records.length === 0 ? (
|
||||||
<div className="bg-paper p-8 rounded-lg border border-line text-center space-y-3">
|
<div className="bg-paper p-8 rounded-lg border border-line text-center space-y-3">
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import Reveal from "@/components/Reveal";
|
||||||
|
import Counter from "@/components/Counter";
|
||||||
import { PUB_CATEGORIES } from "../../../content/categories";
|
import { PUB_CATEGORIES } from "../../../content/categories";
|
||||||
import { PubCategory } from "../../../content/types";
|
import { PubCategory } from "../../../content/types";
|
||||||
|
|
||||||
@@ -24,6 +26,7 @@ export function CategoryNav({ currentCategory, counts }: CategoryNavProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
|
<Reveal variant="up">
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-cobalt-dark">
|
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-cobalt-dark">
|
||||||
Categories
|
Categories
|
||||||
@@ -37,16 +40,22 @@ export function CategoryNav({ currentCategory, counts }: CategoryNavProps) {
|
|||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</Reveal>
|
||||||
|
|
||||||
<div className="grid gap-5 sm:gap-6 md:grid-cols-3">
|
<div className="grid gap-5 sm:gap-6 md:grid-cols-3">
|
||||||
{PUB_CATEGORIES.map((cat) => {
|
{PUB_CATEGORIES.map((cat, idx) => {
|
||||||
const count = getCount(cat.slug);
|
const count = getCount(cat.slug);
|
||||||
const isActive = currentCategory === cat.slug;
|
const isActive = currentCategory === cat.slug;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<Reveal
|
||||||
key={cat.slug}
|
key={cat.slug}
|
||||||
className={`flex flex-col justify-between bg-paper p-5 sm:p-6 rounded-lg transition-all ${
|
variant="up"
|
||||||
|
delay={idx * 80}
|
||||||
|
className="h-full"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={`flex flex-col justify-between bg-paper p-5 sm:p-6 rounded-lg transition-all h-full ${
|
||||||
isActive
|
isActive
|
||||||
? "border-2 border-cobalt-dark shadow-md"
|
? "border-2 border-cobalt-dark shadow-md"
|
||||||
: "border border-line shadow-sm hover:border-cobalt-dark/40"
|
: "border border-line shadow-sm hover:border-cobalt-dark/40"
|
||||||
@@ -84,7 +93,7 @@ export function CategoryNav({ currentCategory, counts }: CategoryNavProps) {
|
|||||||
|
|
||||||
<div className="mt-6 pt-4 border-t border-line flex items-baseline justify-between">
|
<div className="mt-6 pt-4 border-t border-line flex items-baseline justify-between">
|
||||||
<span className="font-display text-3xl font-bold text-ink">
|
<span className="font-display text-3xl font-bold text-ink">
|
||||||
{count}
|
<Counter value={count} />
|
||||||
<span className="text-xs font-mono text-ink-mute ml-1 font-normal">
|
<span className="text-xs font-mono text-ink-mute ml-1 font-normal">
|
||||||
{cat.slug === "patent" ? "Patents" : "Papers"}
|
{cat.slug === "patent" ? "Patents" : "Papers"}
|
||||||
</span>
|
</span>
|
||||||
@@ -105,6 +114,7 @@ export function CategoryNav({ currentCategory, counts }: CategoryNavProps) {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Reveal>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
|
import Reveal from "@/components/Reveal";
|
||||||
import { CategoryNav } from "./_components/CategoryNav";
|
import { CategoryNav } from "./_components/CategoryNav";
|
||||||
import { getPublications, getPatents } from "../../lib/api";
|
import { getPublications, getPatents } from "../../lib/api";
|
||||||
import type { Publication, Patent } from "../../content/types";
|
import type { Publication, Patent } from "../../content/types";
|
||||||
@@ -31,9 +32,11 @@ export default function PublicationsIndexPage() {
|
|||||||
<div className="container-content py-8 sm:py-12 space-y-10 sm:space-y-14">
|
<div className="container-content py-8 sm:py-12 space-y-10 sm:space-y-14">
|
||||||
{/* Page Header */}
|
{/* Page Header */}
|
||||||
<section>
|
<section>
|
||||||
|
<Reveal variant="up">
|
||||||
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink tracking-tight">
|
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink tracking-tight">
|
||||||
PUBLICATIONS
|
PUBLICATIONS
|
||||||
</h1>
|
</h1>
|
||||||
|
</Reveal>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Category Summary Cards */}
|
{/* Category Summary Cards */}
|
||||||
@@ -43,13 +46,21 @@ export default function PublicationsIndexPage() {
|
|||||||
|
|
||||||
{/* Highlights Section */}
|
{/* Highlights Section */}
|
||||||
<section className="space-y-6 pt-6 md:pt-8 border-t border-line">
|
<section className="space-y-6 pt-6 md:pt-8 border-t border-line">
|
||||||
|
<Reveal variant="up">
|
||||||
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-cobalt-dark">
|
<h2 className="font-serif text-2xl sm:text-3xl font-normal text-cobalt-dark">
|
||||||
Highlights
|
Highlights
|
||||||
</h2>
|
</h2>
|
||||||
|
</Reveal>
|
||||||
|
|
||||||
<div className="divide-y divide-line border border-line bg-paper rounded-lg overflow-hidden shadow-sm">
|
<div className="divide-y divide-line border border-line bg-paper rounded-lg overflow-hidden shadow-sm">
|
||||||
{highlights.map((pub, idx) => (
|
{highlights.map((pub, idx) => (
|
||||||
<article key={`${pub.category}-${idx}`} className="p-5 hover:bg-ivory/60 transition-colors space-y-2">
|
<Reveal
|
||||||
|
key={`${pub.category}-${idx}`}
|
||||||
|
as="article"
|
||||||
|
variant="up"
|
||||||
|
delay={Math.min(idx, 6) * 60}
|
||||||
|
className="p-5 hover:bg-ivory/60 transition-colors space-y-2"
|
||||||
|
>
|
||||||
<div className="flex flex-wrap items-center gap-2 text-xs font-mono text-ink-mute">
|
<div className="flex flex-wrap items-center gap-2 text-xs font-mono text-ink-mute">
|
||||||
<span className="px-2 py-0.5 rounded bg-ivory border border-line text-ink font-bold">
|
<span className="px-2 py-0.5 rounded bg-ivory border border-line text-ink font-bold">
|
||||||
{pub.publishedAt?.substring(0, 7) || "Recent"}
|
{pub.publishedAt?.substring(0, 7) || "Recent"}
|
||||||
@@ -76,7 +87,7 @@ export default function PublicationsIndexPage() {
|
|||||||
{pub.venue}{pub.volume ? `, ${pub.volume}` : ""}
|
{pub.venue}{pub.volume ? `, ${pub.volume}` : ""}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</article>
|
</Reveal>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
|
import Reveal from "@/components/Reveal";
|
||||||
import { getStandardsBodies } from "../../lib/api";
|
import { getStandardsBodies } from "../../lib/api";
|
||||||
import type { StandardsBody } from "../../content/types";
|
import type { StandardsBody } from "../../content/types";
|
||||||
|
|
||||||
@@ -16,15 +17,22 @@ export default function StandardizationPage() {
|
|||||||
<div className="container-content py-8 sm:py-12 space-y-10 sm:space-y-14">
|
<div className="container-content py-8 sm:py-12 space-y-10 sm:space-y-14">
|
||||||
{/* Page Header */}
|
{/* Page Header */}
|
||||||
<section>
|
<section>
|
||||||
|
<Reveal variant="up">
|
||||||
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink tracking-tight">
|
<h1 className="font-serif text-4xl sm:text-5xl font-normal text-ink tracking-tight">
|
||||||
International Standardization
|
International Standardization
|
||||||
</h1>
|
</h1>
|
||||||
|
</Reveal>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Standards Bodies Section */}
|
{/* Standards Bodies Section */}
|
||||||
<section className="space-y-8 sm:space-y-10 pt-6 md:pt-8 border-t border-line">
|
<section className="space-y-8 sm:space-y-10 pt-6 md:pt-8 border-t border-line">
|
||||||
{standardsBodies.map((body, idx) => (
|
{standardsBodies.map((body, idx) => (
|
||||||
<div key={idx} className="bg-paper p-6 sm:p-8 rounded-lg border border-line space-y-6 shadow-sm">
|
<Reveal
|
||||||
|
key={idx}
|
||||||
|
variant="up"
|
||||||
|
delay={idx * 120}
|
||||||
|
>
|
||||||
|
<div className="bg-paper p-6 sm:p-8 rounded-lg border border-line space-y-6 shadow-sm">
|
||||||
<div className="flex flex-wrap justify-between items-start gap-4 border-b border-line pb-4">
|
<div className="flex flex-wrap justify-between items-start gap-4 border-b border-line pb-4">
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
@@ -105,6 +113,7 @@ export default function StandardizationPage() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Reveal>
|
||||||
))}
|
))}
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useEffect, useRef, type ElementType, type ReactNode } from "react";
|
import { useEffect, useRef, type ElementType, type ReactNode, type HTMLAttributes } from "react";
|
||||||
|
|
||||||
type Variant = "up" | "left" | "right" | "scale";
|
type Variant = "up" | "left" | "right" | "scale";
|
||||||
|
|
||||||
@@ -18,7 +18,9 @@ export default function Reveal({
|
|||||||
delay = 0,
|
delay = 0,
|
||||||
as: Tag = "div",
|
as: Tag = "div",
|
||||||
className = "",
|
className = "",
|
||||||
}: {
|
style,
|
||||||
|
...rest
|
||||||
|
}: HTMLAttributes<HTMLElement> & {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
variant?: Variant;
|
variant?: Variant;
|
||||||
delay?: number;
|
delay?: number;
|
||||||
@@ -47,12 +49,17 @@ export default function Reveal({
|
|||||||
return () => io.disconnect();
|
return () => io.disconnect();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const combinedStyle = delay
|
||||||
|
? { ...style, transitionDelay: `${delay}ms` }
|
||||||
|
: style;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tag
|
<Tag
|
||||||
ref={ref}
|
ref={ref}
|
||||||
data-variant={variant}
|
data-variant={variant}
|
||||||
style={delay ? { transitionDelay: `${delay}ms` } : undefined}
|
style={combinedStyle}
|
||||||
className={`reveal ${className}`}
|
className={`reveal ${className}`}
|
||||||
|
{...rest}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</Tag>
|
</Tag>
|
||||||
|
|||||||
Reference in New Issue
Block a user