mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-08-24 07:12:18 +10:00
feat: add semantic CSS stylesheets (#3274)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor Agent
parent
4ac19f81b3
commit
d2ffbf9618
@@ -1,12 +1,16 @@
|
||||
import type { Template } from "@reactive-resume/schema/templates";
|
||||
import type { CSSProperties } from "react";
|
||||
import type { ResolvedResumePreviewProps } from "./preview.shared";
|
||||
import type { PreviewPageSize } from "./preview.shared.utils";
|
||||
import { t } from "@lingui/core/macro";
|
||||
import { AnimatePresence, m } from "motion/react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { isRTL } from "@reactive-resume/utils/locale";
|
||||
import { cn } from "@reactive-resume/utils/style";
|
||||
import { createResumePdfBlob } from "@/features/resume/export/pdf-document";
|
||||
import { usePreviewPausedStore, useResumeData } from "../builder/draft";
|
||||
import { useStylesheetStore } from "@/features/resume/stylesheet/store";
|
||||
import { usePreviewPausedStore, useResumeData, useResumeStore } from "../builder/draft";
|
||||
import { PdfCanvasDocument, PdfCanvasPage } from "./pdf-canvas";
|
||||
import { ResumePreviewLoader } from "./preview.shared";
|
||||
import { getResumePreviewGapValue, getResumePreviewPageCount } from "./preview.shared.utils";
|
||||
@@ -19,18 +23,20 @@ type PreviewPdf = {
|
||||
pageSizes: Record<number, PreviewPageSize>;
|
||||
phase: "active" | "exiting" | "staged";
|
||||
renderedPages: number[];
|
||||
template: Template;
|
||||
};
|
||||
|
||||
const UPDATE_DEBOUNCE_MS = 100;
|
||||
const CROSSFADE_DURATION_MS = 180;
|
||||
|
||||
const createPreviewPdf = (file: Blob, id: number, hasExistingPreview: boolean): PreviewPdf => ({
|
||||
const createPreviewPdf = (file: Blob, id: number, hasExistingPreview: boolean, template: Template): PreviewPdf => ({
|
||||
file,
|
||||
id,
|
||||
numPages: 0,
|
||||
pageSizes: {},
|
||||
phase: hasExistingPreview ? "staged" : "active",
|
||||
renderedPages: [],
|
||||
template,
|
||||
});
|
||||
|
||||
const addPreviewLayer = (layers: PreviewPdf[], nextPdf: PreviewPdf) => {
|
||||
@@ -96,6 +102,17 @@ export function ResumePreviewClient({
|
||||
}: ResolvedResumePreviewProps) {
|
||||
const builderResumeData = useResumeData();
|
||||
const resumeData = data ?? builderResumeData;
|
||||
const builderResumeId = useResumeStore((state) => state.resumeId);
|
||||
const stylesheetResumeId = useStylesheetStore((state) => state.resumeId);
|
||||
const mode = useStylesheetStore((state) => state.mode);
|
||||
const applied = useStylesheetStore((state) => state.applied);
|
||||
const presentation = useMemo(
|
||||
() =>
|
||||
data === undefined && builderResumeId !== undefined && stylesheetResumeId === builderResumeId
|
||||
? { stylesheet: { mode, applied } }
|
||||
: undefined,
|
||||
[applied, builderResumeId, data, mode, stylesheetResumeId],
|
||||
);
|
||||
const paused = usePreviewPausedStore((state) => state.paused);
|
||||
|
||||
const [previewLayers, setPreviewLayers] = useState<PreviewPdf[]>([]);
|
||||
@@ -116,15 +133,25 @@ export function ResumePreviewClient({
|
||||
const generatePdfPreview = async () => {
|
||||
try {
|
||||
if (cancelled || requestId !== requestIdRef.current) return;
|
||||
const blob = await createResumePdfBlob(resumeData);
|
||||
const blob = await createResumePdfBlob(resumeData, undefined, undefined, presentation);
|
||||
|
||||
if (!cancelled && requestId === requestIdRef.current) {
|
||||
const nextPdf = createPreviewPdf(blob, pdfIdRef.current++, hasPreviewRef.current);
|
||||
const nextPdf = createPreviewPdf(
|
||||
blob,
|
||||
pdfIdRef.current++,
|
||||
hasPreviewRef.current,
|
||||
resumeData.metadata.template,
|
||||
);
|
||||
|
||||
hasPreviewRef.current = true;
|
||||
setPreviewLayers((current) => addPreviewLayer(current, nextPdf));
|
||||
}
|
||||
} catch {}
|
||||
} catch {
|
||||
if (cancelled || requestId !== requestIdRef.current) return;
|
||||
toast.error(t`The resume preview could not be updated. The last valid preview is still shown.`, {
|
||||
id: "resume-preview-render-error",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const timeoutId = window.setTimeout(() => {
|
||||
@@ -135,7 +162,7 @@ export function ResumePreviewClient({
|
||||
cancelled = true;
|
||||
window.clearTimeout(timeoutId);
|
||||
};
|
||||
}, [resumeData, paused]);
|
||||
}, [paused, presentation, resumeData]);
|
||||
|
||||
if (!resumeData) return null;
|
||||
|
||||
@@ -166,6 +193,7 @@ export function ResumePreviewClient({
|
||||
<m.div
|
||||
key={visiblePdf.id}
|
||||
aria-hidden={visiblePdf.phase !== "active"}
|
||||
data-resume-preview-template={visiblePdf.template}
|
||||
style={{ "--resume-preview-page-gap": resolvedPageGap } as CSSProperties}
|
||||
className={cn("col-start-1 row-start-1", visiblePdf.phase !== "active" && "pointer-events-none")}
|
||||
initial={{ opacity: visiblePdf.phase === "active" ? 1 : 0 }}
|
||||
|
||||
Reference in New Issue
Block a user