style: resume template style perf

This commit is contained in:
JOYCEQL
2024-12-15 23:58:41 +08:00
committed by qingchen
parent 21b17d0c0f
commit a16926faea
5 changed files with 174 additions and 13 deletions
+4 -2
View File
@@ -10,7 +10,8 @@
--card-foreground: 224 71.4% 4.1%;
--popover: 0 0% 100%;
--popover-foreground: 224 71.4% 4.1%;
--primary: 262.1 83.3% 57.8%;
/* --primary: 262.1 83.3% 57.8%; */
--primary: 240 5.9% 10%;
--primary-foreground: 210 20% 98%;
--secondary: 220 14.3% 95.9%;
--secondary-foreground: 220.9 39.3% 11%;
@@ -22,7 +23,8 @@
--destructive-foreground: 210 20% 98%;
--border: 220 13% 91%;
--input: 220 13% 91%;
--ring: 262.1 83.3% 57.8%;
/* --ring: 262.1 83.3% 57.8%; */
--ring: 240 5.9% 10%;
--radius: 0.75rem;
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
@@ -0,0 +1,106 @@
import { cn } from "@/lib/utils";
import React, { PropsWithChildren } from "react";
import { motion } from "framer-motion";
interface DockProps extends PropsWithChildren<React.HTMLAttributes<HTMLDivElement>> {
className?: string;
}
export function Dock({ children, className, ...props }: DockProps) {
// Convert children to array to handle them
const childrenArray = React.Children.toArray(children);
// Find the index of TemplateSheet for splitting
const templateSheetIndex = childrenArray.findIndex((child) => {
if (React.isValidElement(child)) {
const tooltip = child.props.children;
if (React.isValidElement(tooltip)) {
const trigger = tooltip.props.children.find(
(child: any) => child?.type?.name === "TooltipTrigger"
);
if (trigger) {
const content = trigger.props.children;
if (React.isValidElement(content)) {
const icon = content.props.children;
return React.isValidElement(icon) && icon.type?.name === "TemplateSheet";
}
}
}
}
return false;
});
// If TemplateSheet is not found, render all children in a single group
if (templateSheetIndex === -1) {
return (
<div
{...props}
className={cn(
"flex flex-col items-center gap-4 rounded-xl bg-white/[0.7] p-4 shadow-lg backdrop-blur-md dark:bg-slate-800/[0.7] dark:shadow-slate-900/20",
className
)}
>
<div className="flex flex-col items-center gap-4">{children}</div>
</div>
);
}
// Split children into three groups
const topChildren = childrenArray.slice(0, templateSheetIndex);
const middleChild = childrenArray[templateSheetIndex];
const bottomChildren = childrenArray.slice(templateSheetIndex + 1);
return (
<div
{...props}
className={cn(
"flex flex-col items-center gap-4 rounded-xl bg-white/[0.7] p-4 shadow-lg backdrop-blur-md dark:bg-slate-800/[0.7] dark:shadow-slate-900/20",
className
)}
>
{/* Top group */}
{topChildren.length > 0 && (
<div className="flex flex-col items-center gap-4">{topChildren}</div>
)}
{/* Decorative line */}
{topChildren.length > 0 && (
<div className="w-8 h-px bg-gradient-to-r from-transparent via-slate-300 to-transparent dark:via-slate-600" />
)}
{/* Middle (TemplateSheet) */}
{middleChild}
{/* Decorative line */}
{bottomChildren.length > 0 && (
<div className="w-8 h-px bg-gradient-to-r from-transparent via-slate-300 to-transparent dark:via-slate-600" />
)}
{/* Bottom group */}
{bottomChildren.length > 0 && (
<div className="flex flex-col items-center gap-4">{bottomChildren}</div>
)}
</div>
);
}
interface DockIconProps extends PropsWithChildren {
className?: string;
onClick?: () => void;
}
export function DockIcon({ children, className, onClick }: DockIconProps) {
return (
<motion.div
whileHover={{ scale: 1.2 }}
whileTap={{ scale: 0.95 }}
className={cn(
"flex size-10 items-center justify-center rounded-lg bg-white text-neutral-700 shadow-sm transition-colors hover:bg-gray-100 dark:bg-slate-800 dark:text-slate-200 dark:hover:bg-slate-700 dark:shadow-slate-900/20",
className
)}
onClick={onClick}
>
{children}
</motion.div>
);
}
+60 -3
View File
@@ -1,12 +1,20 @@
"use client";
import React, { useEffect, useMemo, useState } from "react";
import React, { useEffect, useMemo, useState, useRef } from "react";
import { throttle } from "lodash";
import TemplateSheet from "@/components/shared/TemplateSheet";
import { Dock, DockIcon } from "@/components/magicui/dock";
import ResumeTemplateComponent from "../templates";
import { DEFAULT_TEMPLATES } from "@/config";
import { DEFAULT_TEMPLATES, GITHUB_REPO_URL } from "@/config";
import { cn } from "@/lib/utils";
import { useResumeStore } from "@/store/useResumeStore";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
export type IconProps = React.HTMLAttributes<SVGElement>;
const getFontFamilyClass = (fontFamily: string) => {
switch (fontFamily) {
case "serif":
@@ -54,6 +62,17 @@ const PageBreakLine = React.memo(({ pageNumber }: PageBreakLineProps) => {
PageBreakLine.displayName = "PageBreakLine";
const Icons = {
gitHub: (props: IconProps) => (
<svg viewBox="0 0 438.549 438.549" {...props}>
<path
fill="currentColor"
d="M409.132 114.573c-19.608-33.596-46.205-60.194-79.798-79.8-33.598-19.607-70.277-29.408-110.063-29.408-39.781 0-76.472 9.804-110.063 29.408-33.596 19.605-60.192 46.204-79.8 79.8C9.803 148.168 0 184.854 0 224.63c0 47.78 13.94 90.745 41.827 128.906 27.884 38.164 63.906 64.572 108.063 79.227 5.14.954 8.945.283 11.419-1.996 2.475-2.282 3.711-5.14 3.711-8.562 0-.571-.049-5.708-.144-15.417a2549.81 2549.81 0 01-.144-25.406l-6.567 1.136c-4.187.767-9.469 1.092-15.846 1-6.374-.089-12.991-.757-19.842-1.999-6.854-1.231-13.229-4.086-19.13-8.559-5.898-4.473-10.085-10.328-12.56-17.556l-2.855-6.57c-1.903-4.374-4.899-9.233-8.992-14.559-4.093-5.331-8.232-8.945-12.419-10.848l-1.999-1.431c-1.332-.951-2.568-2.098-3.711-3.429-1.142-1.331-1.997-2.663-2.568-3.997-.572-1.335-.098-2.43 1.427-3.289 1.525-.859 4.281-1.276 8.28-1.276l5.708.853c3.807.763 8.516 3.042 14.133 6.851 5.614 3.806 10.229 8.754 13.846 14.842 4.38 7.806 9.657 13.754 15.846 17.847 6.184 4.093 12.419 6.136 18.699 6.136 6.28 0 11.704-.476 16.274-1.423 4.565-.952 8.848-2.383 12.847-4.285 1.713-12.758 6.377-22.559 13.988-29.41-10.848-1.14-20.601-2.857-29.264-5.14-8.658-2.286-17.605-5.996-26.835-11.14-9.235-5.137-16.896-11.516-22.985-19.126-6.09-7.614-11.088-17.61-14.987-29.979-3.901-12.374-5.852-26.648-5.852-42.826 0-23.035 7.52-42.637 22.557-58.817-7.044-17.318-6.379-36.732 1.997-58.24 5.52-1.715 13.706-.428 24.554 3.853 10.85 4.283 18.794 7.952 23.84 10.994 5.046 3.041 9.089 5.618 12.135 7.708 17.705-4.947 35.976-7.421 54.818-7.421s37.117 2.474 54.823 7.421l10.849-6.849c7.419-4.57 16.18-8.758 26.262-12.565 10.088-3.805 17.802-4.853 23.134-3.138 8.562 21.509 9.325 40.922 2.279 58.24 15.036 16.18 22.559 35.787 22.559 58.817 0 16.178-1.958 30.497-5.853 42.966-3.9 12.471-8.941 22.457-15.125 29.979-6.191 7.521-13.901 13.85-23.131 18.986-9.232 5.14-18.182 8.85-26.84 11.136-8.662 2.286-18.415 4.004-29.263 5.146 9.894 8.562 14.842 22.077 14.842 40.539v60.237c0 3.422 1.19 6.279 3.572 8.562 2.379 2.279 6.136 2.95 11.276 1.995 44.163-14.653 80.185-41.062 108.068-79.226 27.88-38.161 41.825-81.126 41.825-128.906-.01-39.771-9.818-76.454-29.414-110.049z"
></path>
</svg>
),
};
const PreviewPanel = () => {
const { activeResume } = useResumeStore();
const template = useMemo(() => {
@@ -63,6 +82,9 @@ const PreviewPanel = () => {
);
}, [activeResume?.templateId]);
const startRef = useRef<HTMLDivElement>(null);
const endRef = useRef<HTMLDivElement>(null);
const previewRef = React.useRef<HTMLDivElement>(null);
const resumeContentRef = React.useRef<HTMLDivElement>(null);
const [contentHeight, setContentHeight] = useState(0);
@@ -97,6 +119,10 @@ const PreviewPanel = () => {
if (!activeResume) return null;
const handleGoGitHub = () => {
window.open(GITHUB_REPO_URL, "_blank");
};
return (
<div
ref={previewRef}
@@ -104,6 +130,7 @@ const PreviewPanel = () => {
>
<div className="py-4 px-4 min-h-screen flex justify-center scale-90 origin-top-left">
<div
ref={startRef}
className={cn(
"w-[210mm] min-h-[297mm]",
"bg-white",
@@ -125,7 +152,37 @@ const PreviewPanel = () => {
</div>
</div>
</div>
<TemplateSheet />
<div className="fixed top-1/2 right-6 transform -translate-y-1/2">
<TooltipProvider delayDuration={0}>
<Dock>
<DockIcon>
<Tooltip>
<TooltipTrigger asChild>
<div className="cursor-pointer" onClick={handleGoGitHub}>
<Icons.gitHub className="size-6" />
</div>
</TooltipTrigger>
<TooltipContent side="left" sideOffset={10}>
<p>Go GitHub</p>
</TooltipContent>
</Tooltip>
</DockIcon>
<DockIcon>
<Tooltip>
<TooltipTrigger asChild>
<div className="cursor-pointer">
<TemplateSheet></TemplateSheet>
</div>
</TooltipTrigger>
<TooltipContent side="left" sideOffset={10}>
<p></p>
</TooltipContent>
</Tooltip>
</DockIcon>
</Dock>
</TooltipProvider>
</div>
</div>
);
};
@@ -1,4 +1,4 @@
import { Layout, LayoutTemplate } from "lucide-react";
import { Layout, PanelsLeftBottom } from "lucide-react";
import { motion } from "framer-motion";
import {
Sheet,
@@ -21,13 +21,7 @@ const TemplateSheet = () => {
return (
<Sheet>
<SheetTrigger asChild>
<Button
size="icon"
variant="secondary"
className="absolute top-[16px] right-6 shadow-lg"
>
<LayoutTemplate className="w-4 h-4" />
</Button>
<PanelsLeftBottom size={24} />
</SheetTrigger>
<SheetContent side="left" className="w-1/2 sm:max-w-1/2">
<SheetHeader>
+2
View File
@@ -74,3 +74,5 @@ export const DEFAULT_TEMPLATES: ResumeTemplate[] = [
},
},
];
export const GITHUB_REPO_URL = "https://github.com/JOYCEQL/magic-resume";