fix height problems
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import type { ReactNode } from "react"
|
||||
import CvEntry from "./CvEntry"
|
||||
import type { RouterOutputs } from "~/server/routers/_app"
|
||||
import { cn } from "~/lib/utils"
|
||||
@@ -13,8 +14,9 @@ type CvCategoryProps = {
|
||||
category: CvCategoryData,
|
||||
layout: "row" | "col",
|
||||
position?: number,
|
||||
descriptions: Record<string, ReactNode>,
|
||||
}
|
||||
export default function CvCategory({ category, layout, position = 0 }: CvCategoryProps) {
|
||||
export default function CvCategory({ category, layout, position = 0, descriptions }: CvCategoryProps) {
|
||||
const entries = category.cvEntry
|
||||
return (
|
||||
<AnimatedCard position={position} className={cn(layout == "row" ? "w-full" : "", "h-screen")}>
|
||||
@@ -28,7 +30,7 @@ export default function CvCategory({ category, layout, position = 0 }: CvCategor
|
||||
<ScrollArea>
|
||||
{entries.map((entry, i) => (
|
||||
<AnimatePopUp position={position + 0.4 + i * 0.2} debugId={`cv-entry-wrapper:${category.name}:${entry.title}:${position + 0.4 + i * 0.2}`} key={entry.id}>
|
||||
<CvEntry position={position + 0.4 + i * 0.2} entry={entry} className={layout == "row" ? "w-full lg:w-fit" : undefined} />
|
||||
<CvEntry position={position + 0.4 + i * 0.2} entry={entry} description={descriptions[entry.id]} className={layout == "row" ? "w-full lg:w-fit" : undefined} />
|
||||
</AnimatePopUp>
|
||||
))}
|
||||
</ScrollArea>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import type { ReactNode } from "react"
|
||||
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "~/components/ui/card"
|
||||
import { cn } from "~/lib/utils"
|
||||
import { format } from 'date-fns'
|
||||
import type { ArrayElement } from "type-fest"
|
||||
import AnimateTextIn from "~/app/_components/Animated/AnimateIn"
|
||||
import AnimatePopUp from "~/app/_components/Animated/AnimatePopUp"
|
||||
import { MDXRemote } from "next-mdx-remote/rsc";
|
||||
import AnimatedDiv from "~/app/_components/Animated/AnimatedDiv"
|
||||
import type { CvCategoryData } from "./CvCategory"
|
||||
import { ClientMdx } from "~/components/ClientMdx"
|
||||
|
||||
export type CvEntryData = ArrayElement<CvCategoryData['cvEntry']>
|
||||
|
||||
export default function CvEntry({ entry, className, position = 0 }: {
|
||||
export default function CvEntry({ entry, description, className, position = 0 }: {
|
||||
entry: CvEntryData,
|
||||
description?: ReactNode,
|
||||
className?: string,
|
||||
position?: number
|
||||
}) {
|
||||
@@ -27,11 +27,15 @@ export default function CvEntry({ entry, className, position = 0 }: {
|
||||
}
|
||||
{entry.description ?
|
||||
<CardContent className="text-sm lg:text-base">
|
||||
<AnimatePopUp once position={position + 0.2} debugId={`cv-entry-description:${entry.title}:${position + 0.2}`}>
|
||||
{/* Fade the description in place instead of collapsing its height:
|
||||
the outer entry pop-up (CvCategory) measures height:auto when it
|
||||
plays, so the description must stay laid out at full height or the
|
||||
entry reveals too short. */}
|
||||
<AnimatedDiv once position={position + 0.2} className="opacity-0" opacity={1} duration={0.5} debugId={`cv-entry-description:${entry.title}:${position + 0.2}`}>
|
||||
<article className="prose prose-zinc dark:prose-invert max-w-none">
|
||||
<ClientMdx source={entry.description} fallback={entry.description} />
|
||||
{description ?? entry.description}
|
||||
</article>
|
||||
</AnimatePopUp>
|
||||
</AnimatedDiv>
|
||||
</CardContent> :
|
||||
<></>
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
'use client'
|
||||
import type { ReactNode } from "react";
|
||||
import { Sidebar, SidebarContent, SidebarProvider } from "~/components/ui/sidebar";
|
||||
import type { RouterOutputs } from "~/server/routers/_app"
|
||||
import SidebarTriggerDisappearsOnMobile from "./SidebarTriggerDisappearsOnMobile";
|
||||
import CvCategory from "./CvCategory";
|
||||
import { useTimeLine } from "~/app/_providers/GsapProvicer";
|
||||
export default function CvPage(props: { cv: RouterOutputs['categoryv2']['listAllWithEntries'] }) {
|
||||
export default function CvPage(props: {
|
||||
cv: RouterOutputs['categoryv2']['listAllWithEntries'],
|
||||
descriptions: Record<string, ReactNode>,
|
||||
}) {
|
||||
useTimeLine(props.cv)
|
||||
const { descriptions } = props
|
||||
const byPosition = (pos: "sidebar" | "header" | "col1" | "col2") =>
|
||||
props.cv?.filter((c) => c.layoutPosition === pos) ?? []
|
||||
const sidebarCategories = byPosition("sidebar")
|
||||
@@ -21,7 +26,7 @@ export default function CvPage(props: { cv: RouterOutputs['categoryv2']['listAll
|
||||
<Sidebar>
|
||||
<SidebarContent className="p-2 lg:pt-[3.2rem]">
|
||||
{sidebarCategories.map((cat, i) => (
|
||||
<CvCategory layout="col" position={i} category={cat} key={cat.id} />
|
||||
<CvCategory layout="col" position={i} category={cat} descriptions={descriptions} key={cat.id} />
|
||||
))}
|
||||
</SidebarContent>
|
||||
</Sidebar>
|
||||
@@ -31,18 +36,18 @@ export default function CvPage(props: { cv: RouterOutputs['categoryv2']['listAll
|
||||
<div id="mainwrap" className="flex w-full flex-col gap-4 lg:px-[15vw]">
|
||||
<div id="header" className="flex w-full h-fit flex-row gap-4 flex-wrap">
|
||||
{headerCategories.map((cat, i) => (
|
||||
<CvCategory layout="row" position={i} category={cat} key={cat.id} />
|
||||
<CvCategory layout="row" position={i} category={cat} descriptions={descriptions} key={cat.id} />
|
||||
))}
|
||||
</div>
|
||||
<div id="colwrapper" className="flex flex-col lg:flex-row w-full h-3/4 gap-4">
|
||||
<div id="col1" className={`flex flex-col w-full ${col1Categories.length > 0 ? "lg:w-1/2" : ""} h-full gap-4`}>
|
||||
{col1Categories.map((cat, i) => (
|
||||
<CvCategory layout="col" position={i} category={cat} key={cat.id} />
|
||||
<CvCategory layout="col" position={i} category={cat} descriptions={descriptions} key={cat.id} />
|
||||
))}
|
||||
</div>
|
||||
<div id="col2" className={`flex flex-col w-full ${col2Categories.length > 0 ? "lg:w-1/2" : ""} h-full gap-4`}>
|
||||
{col2Categories.map((cat, i) => (
|
||||
<CvCategory layout="col" position={i} category={cat} key={cat.id} />
|
||||
<CvCategory layout="col" position={i} category={cat} descriptions={descriptions} key={cat.id} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,42 @@
|
||||
import { Suspense } from "react";
|
||||
import { Suspense, type ReactNode } from "react";
|
||||
import { MDXRemote } from "next-mdx-remote/rsc";
|
||||
import rehypeHighlight from "rehype-highlight";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import { servTrpc as trpc } from "../_trpc/ServerClient";
|
||||
import { mdxComponents } from "~/components/mdx-components";
|
||||
import Page from "./_components/Page";
|
||||
|
||||
export default async function CvPage() {
|
||||
const cv = await trpc.categoryv2.listAllWithEntries();
|
||||
|
||||
// Render the MDX descriptions on the server so they exist at first paint.
|
||||
// The client tree (which runs the GSAP entrance via useTimeLine) only places
|
||||
// these already-rendered nodes — it never invokes the MDX renderer itself, so
|
||||
// the 'use client' boundary stays intact and the animations no longer play
|
||||
// against an un-rendered fallback.
|
||||
const descriptions: Record<string, ReactNode> = {};
|
||||
for (const category of cv ?? []) {
|
||||
for (const entry of category.cvEntry) {
|
||||
if (!entry.description?.trim()) continue;
|
||||
descriptions[entry.id] = (
|
||||
<MDXRemote
|
||||
source={entry.description}
|
||||
components={mdxComponents}
|
||||
options={{
|
||||
mdxOptions: {
|
||||
format: "md",
|
||||
remarkPlugins: [remarkGfm],
|
||||
rehypePlugins: [rehypeHighlight],
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Suspense>
|
||||
<Page cv={cv}/>
|
||||
<Page cv={cv} descriptions={descriptions} />
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user