perf: 🎨 remove fragment imports, optimize templates

This commit is contained in:
Amruth Pillai
2023-11-06 22:37:32 +01:00
parent 2d35057e57
commit fca61543c5
42 changed files with 742 additions and 661 deletions

View File

@ -33,7 +33,7 @@ export const BuilderLayout = () => {
if (isDesktop) {
return (
<div className="relative h-full w-full overflow-hidden">
<PanelGroup direction="horizontal">
<PanelGroup direction="horizontal" autoSaveId="builder-layout">
<Panel
collapsible
minSize={20}

View File

@ -6,7 +6,7 @@ import {
PageGrid,
PageNumber,
PageWrapper,
Rhyhorn,
templatesList,
} from "@reactive-resume/templates";
import { pageSizeMap } from "@reactive-resume/utils";
import { AnimatePresence, motion } from "framer-motion";
@ -35,6 +35,15 @@ export const BuilderPage = () => {
};
}, [resume.metadata.page]);
const Template = useMemo(() => {
const Component = templatesList.find((template) => template.id === resume.metadata.template)
?.Component;
if (!Component) return null;
return Component;
}, [resume.metadata.template]);
return (
<>
<Helmet>
@ -64,7 +73,12 @@ export const BuilderPage = () => {
<PageWrapper>
{showPageNumbers && <PageNumber>Page {pageIndex + 1}</PageNumber>}
<Rhyhorn isFirstPage={pageIndex === 0} columns={columns as SectionKey[][]} />
{Template !== null && (
<Template
isFirstPage={pageIndex === 0}
columns={columns as SectionKey[][]}
/>
)}
{showBreakLine && <PageBreakLine $pageHeight={pageHeight} />}
</PageWrapper>

View File

@ -12,7 +12,6 @@ import {
FormMessage,
Input,
RichInput,
Slider,
} from "@reactive-resume/ui";
import { AnimatePresence, motion } from "framer-motion";
import { useForm } from "react-hook-form";
@ -88,13 +87,13 @@ export const CustomSectionDialog = () => {
/>
<FormField
name="url"
name="location"
control={form.control}
render={({ field }) => (
<FormItem className="col-span-1">
<FormLabel>Website</FormLabel>
<FormLabel>Location</FormLabel>
<FormControl>
<URLInput {...field} />
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
@ -102,24 +101,13 @@ export const CustomSectionDialog = () => {
/>
<FormField
name="level"
name="url"
control={form.control}
render={({ field }) => (
<FormItem className="col-span-2">
<FormLabel>Level</FormLabel>
<FormControl className="py-2">
<div className="flex items-center gap-x-4">
<Slider
{...field}
min={0}
max={5}
value={[field.value]}
orientation="horizontal"
onValueChange={(value) => field.onChange(value[0])}
/>
<span className="text-base font-bold">{field.value}</span>
</div>
<FormLabel>Website</FormLabel>
<FormControl>
<URLInput {...field} />
</FormControl>
<FormMessage />
</FormItem>

View File

@ -16,7 +16,7 @@ type CustomFieldProps = {
export const CustomField = ({ field, onChange, onRemove }: CustomFieldProps) => {
const controls = useDragControls();
const handleChange = (key: "name" | "value", value: string) =>
const handleChange = (key: "icon" | "name" | "value", value: string) =>
onChange({ ...field, [key]: value });
return (
@ -28,7 +28,7 @@ export const CustomField = ({ field, onChange, onRemove }: CustomFieldProps) =>
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -50 }}
>
<div className="flex items-end justify-between space-x-4">
<div className="flex items-end justify-between space-x-2">
<Button
size="icon"
variant="ghost"
@ -38,9 +38,15 @@ export const CustomField = ({ field, onChange, onRemove }: CustomFieldProps) =>
<DotsSixVertical />
</Button>
<Input
placeholder="Icon"
value={field.icon}
className="!ml-0"
onChange={(event) => handleChange("icon", event.target.value)}
/>
<Input
placeholder="Name"
className="!ml-2"
value={field.name}
onChange={(event) => handleChange("name", event.target.value)}
/>
@ -54,7 +60,7 @@ export const CustomField = ({ field, onChange, onRemove }: CustomFieldProps) =>
<Button
size="icon"
variant="ghost"
className="!ml-2 shrink-0"
className="!ml-0 shrink-0"
onClick={() => onRemove(field.id)}
>
<X />
@ -73,7 +79,10 @@ export const CustomFieldsSection = ({ className }: Props) => {
const customFields = useResumeStore((state) => state.resume.data.basics.customFields);
const onAddCustomField = () => {
setValue("basics.customFields", [...customFields, { id: createId(), name: "", value: "" }]);
setValue("basics.customFields", [
...customFields,
{ id: createId(), icon: "", name: "", value: "" },
]);
};
const onChangeCustomField = (field: ICustomField) => {

View File

@ -1,4 +1,5 @@
import { Button } from "@reactive-resume/ui";
import { templatesList } from "@reactive-resume/templates";
import { Button, HoverCard, HoverCardContent, HoverCardTrigger } from "@reactive-resume/ui";
import { cn } from "@reactive-resume/utils";
import { useResumeStore } from "@/client/stores/resume";
@ -6,9 +7,6 @@ import { useResumeStore } from "@/client/stores/resume";
import { getSectionIcon } from "../shared/section-icon";
export const TemplateSection = () => {
// TODO: Import templates from @reactive-resume/templates
const templateList = ["rhyhorn"];
const setValue = useResumeStore((state) => state.setValue);
const currentTemplate = useResumeStore((state) => state.resume.data.metadata.template);
@ -21,20 +19,31 @@ export const TemplateSection = () => {
</div>
</header>
<main className="grid grid-cols-2 gap-y-4">
{templateList.map((template) => (
<Button
key={template}
variant="outline"
disabled={template === currentTemplate}
onClick={() => setValue("metadata.template", template)}
className={cn(
"flex h-12 items-center justify-center overflow-hidden rounded border text-center text-sm capitalize ring-primary transition-colors hover:bg-secondary-accent focus:outline-none focus:ring-1 disabled:opacity-100",
template === currentTemplate && "ring-1",
)}
>
{template}
</Button>
<main className="grid grid-cols-2 gap-4">
{templatesList.map(({ id, name }) => (
<HoverCard key={id} openDelay={0} closeDelay={0}>
<HoverCardTrigger asChild>
<Button
variant="outline"
onClick={() => setValue("metadata.template", id)}
className={cn(
"flex h-12 items-center justify-center overflow-hidden rounded border text-center text-sm capitalize ring-primary transition-colors hover:bg-secondary-accent focus:outline-none focus:ring-1 disabled:opacity-100",
id === currentTemplate && "ring-1",
)}
>
{name}
</Button>
</HoverCardTrigger>
<HoverCardContent className="max-w-xs overflow-hidden border-none bg-white p-0">
<img
alt={name}
loading="lazy"
src={`/templates/${id}.jpg`}
className="aspect-[1/1.4142]"
/>
</HoverCardContent>
</HoverCard>
))}
</main>
</section>

View File

@ -48,7 +48,7 @@ export const ThemeSection = () => {
style={{ backgroundColor: theme.primary }}
/>
</PopoverTrigger>
<PopoverContent asChild className="rounded-lg border-none bg-transparent p-0">
<PopoverContent className="rounded-lg border-none bg-transparent p-0">
<HexColorPicker
color={theme.primary}
onChange={(color) => {

View File

@ -1,5 +1,5 @@
import { zodResolver } from "@hookform/resolvers/zod";
import { CaretDown, MagicWand, Plus, TestTube } from "@phosphor-icons/react";
import { CaretDown, Flask, MagicWand, Plus } from "@phosphor-icons/react";
import { createResumeSchema, ResumeDto } from "@reactive-resume/dto";
import { idSchema } from "@reactive-resume/schema";
import {
@ -265,7 +265,7 @@ export const ResumeDialog = () => {
</DropdownMenuTrigger>
<DropdownMenuContent side="right" align="center">
<DropdownMenuItem onClick={onCreateSample}>
<TestTube className="mr-2" />
<Flask className="mr-2" />
Create Sample Resume
</DropdownMenuItem>
</DropdownMenuContent>

View File

@ -1,10 +1,12 @@
import { useTemplate } from "@reactive-resume/hooks";
import { ResumeData, SectionKey } from "@reactive-resume/schema";
import { Artboard, PageWrapper, Rhyhorn } from "@reactive-resume/templates";
import { Artboard, PageWrapper } from "@reactive-resume/templates";
import { Navigate } from "react-router-dom";
import { useSessionStorage } from "usehooks-ts";
export const PrinterPage = () => {
const [resume] = useSessionStorage<ResumeData | null>("resume", null);
const template = useTemplate(resume?.metadata.template);
if (!resume) return <Navigate to="/" replace />;
@ -12,7 +14,9 @@ export const PrinterPage = () => {
<Artboard resume={resume} style={{ pointerEvents: "auto" }}>
{resume.metadata.layout.map((columns, pageIndex) => (
<PageWrapper key={pageIndex} data-page={pageIndex + 1}>
<Rhyhorn isFirstPage={pageIndex === 0} columns={columns as SectionKey[][]} />
{template !== null && (
<template.Component isFirstPage={pageIndex === 0} columns={columns as SectionKey[][]} />
)}
</PageWrapper>
))}
</Artboard>

View File

@ -1,6 +1,7 @@
import { ResumeDto } from "@reactive-resume/dto";
import { useTemplate } from "@reactive-resume/hooks";
import { SectionKey } from "@reactive-resume/schema";
import { Artboard, PageWrapper, Rhyhorn } from "@reactive-resume/templates";
import { Artboard, PageWrapper } from "@reactive-resume/templates";
import { Button } from "@reactive-resume/ui";
import { pageSizeMap } from "@reactive-resume/utils";
import { Helmet } from "react-helmet-async";
@ -15,6 +16,7 @@ import { findResumeByUsernameSlug } from "@/client/services/resume";
export const PublicResumePage = () => {
const { title, data: resume } = useLoaderData() as ResumeDto;
const format = resume.metadata.page.format;
const template = useTemplate(resume.metadata.template);
return (
<div>
@ -29,7 +31,12 @@ export const PublicResumePage = () => {
<Artboard resume={resume} style={{ pointerEvents: "auto" }}>
{resume.metadata.layout.map((columns, pageIndex) => (
<PageWrapper key={pageIndex} data-page={pageIndex + 1}>
<Rhyhorn isFirstPage={pageIndex === 0} columns={columns as SectionKey[][]} />
{template !== null && (
<template.Component
isFirstPage={pageIndex === 0}
columns={columns as SectionKey[][]}
/>
)}
</PageWrapper>
))}
</Artboard>