diff --git a/apps/web/src/components/input/rich-input.tsx b/apps/web/src/components/input/rich-input.tsx index 37b9caa93..2973cce2c 100644 --- a/apps/web/src/components/input/rich-input.tsx +++ b/apps/web/src/components/input/rich-input.tsx @@ -121,8 +121,7 @@ export function RichInput({ value, onChange, style, className, editorClassName, "data-editor": "true", "data-fullscreen": isFullscreen ? "true" : "false", class: cn( - "group/editor overflow-y-auto p-3 pb-4", - "prose prose-sm prose-zinc dark:prose-invert max-w-none", + "wysiwyg group/editor overflow-y-auto p-3 pb-4", "rounded-md rounded-t-none border outline-none focus-visible:border-ring", "[td:has(.selectedCell)]:bg-primary", "data-[fullscreen=false]:max-h-[400px] data-[fullscreen=false]:min-h-[100px]", diff --git a/packages/pdf/src/templates/shared/section-links.test.ts b/packages/pdf/src/templates/shared/section-links.test.ts new file mode 100644 index 000000000..6904ff418 --- /dev/null +++ b/packages/pdf/src/templates/shared/section-links.test.ts @@ -0,0 +1,37 @@ +import { describe, expect, test } from "vitest"; +import { getInlineItemWebsiteUrl, shouldRenderSeparateItemWebsite } from "./section-links"; + +describe("section item website links", () => { + test("uses inline website URL and suppresses the separate website link when inlineLink is true", () => { + const website = { + url: "https://example.com/company", + label: "example.com/company", + inlineLink: true, + }; + + expect(getInlineItemWebsiteUrl(website)).toBe("https://example.com/company"); + expect(shouldRenderSeparateItemWebsite(website)).toBe(false); + }); + + test("renders the website separately when inlineLink is false", () => { + const website = { + url: "https://example.com/company", + label: "example.com/company", + inlineLink: false, + }; + + expect(getInlineItemWebsiteUrl(website)).toBeUndefined(); + expect(shouldRenderSeparateItemWebsite(website)).toBe(true); + }); + + test("does not create an inline or separate link without a URL", () => { + const website = { + url: "", + label: "", + inlineLink: true, + }; + + expect(getInlineItemWebsiteUrl(website)).toBeUndefined(); + expect(shouldRenderSeparateItemWebsite(website)).toBe(false); + }); +}); diff --git a/packages/pdf/src/templates/shared/section-links.ts b/packages/pdf/src/templates/shared/section-links.ts new file mode 100644 index 000000000..10b525012 --- /dev/null +++ b/packages/pdf/src/templates/shared/section-links.ts @@ -0,0 +1,15 @@ +import type { Website } from "@reactive-resume/schema/resume/data"; + +type ItemWebsite = Website & { + inlineLink?: boolean | undefined; +}; + +export const getInlineItemWebsiteUrl = (website: ItemWebsite): string | undefined => { + if (!website.url || !website.inlineLink) return undefined; + + return website.url; +}; + +export const shouldRenderSeparateItemWebsite = (website: ItemWebsite): boolean => { + return Boolean(website.url && !website.inlineLink); +}; diff --git a/packages/pdf/src/templates/shared/sections.tsx b/packages/pdf/src/templates/shared/sections.tsx index 5ab5de66a..e6cc889da 100644 --- a/packages/pdf/src/templates/shared/sections.tsx +++ b/packages/pdf/src/templates/shared/sections.tsx @@ -38,6 +38,7 @@ import { MetaLine } from "./meta-line"; import { getTemplateMetrics } from "./metrics"; import { Bold, Div, Heading, Icon, Link, Small, Text } from "./primitives"; import { RichText } from "./rich-text"; +import { getInlineItemWebsiteUrl, shouldRenderSeparateItemWebsite } from "./section-links"; import { composeStyles } from "./styles"; type SectionItemsContextValue = { @@ -206,6 +207,27 @@ const SectionItemHeader = ({ children }: { children: ReactNode }) => { return {children}; }; +type ItemWebsite = { + url: string; + label: string; + inlineLink?: boolean | undefined; +}; + +const ItemTitle = ({ children, website }: { children: ReactNode; website: ItemWebsite }) => { + const inlineWebsiteUrl = getInlineItemWebsiteUrl(website); + const title = {children}; + + if (!inlineWebsiteUrl) return title; + + return {title}; +}; + +const ItemWebsiteLink = ({ website }: { website: ItemWebsite }) => { + if (!shouldRenderSeparateItemWebsite(website)) return null; + + return {website.label || website.url}; +}; + const SummarySection = ({ showHeading = true }: { showHeading?: boolean } = {}) => { const data = useRender(); const { summary } = data; @@ -272,9 +294,7 @@ const ProfileSection = ({ {item.network} - - {item.username} - + {item.username} ))} @@ -316,7 +336,7 @@ const ExperienceSection = ({ ) : null } - middle={{item.company}} + middle={{item.company}} trailing={{item.period}} /> ); @@ -324,7 +344,7 @@ const ExperienceSection = ({ const renderSplitHeader = () => ( <> - {item.company} + {item.company} {item.location} @@ -355,11 +375,7 @@ const ExperienceSection = ({ {item.roles.length === 0 && {item.description}} - {item.website.url && ( - - {item.website.label || item.website.url} - - )} + ); })} @@ -406,7 +422,7 @@ const EducationSection = ({ ) : null } - middle={{item.school}} + middle={{item.school}} trailing={{item.period}} /> {gradeAndLocation && {gradeAndLocation}} @@ -416,7 +432,7 @@ const EducationSection = ({ const renderSplitHeader = () => ( <> - {item.school} + {item.school} {degreeAndGrade} @@ -433,11 +449,7 @@ const EducationSection = ({ {item.description} - {item.website.url && ( - - {item.website.label || item.website.url} - - )} + ); })} @@ -468,18 +480,14 @@ const ProjectsSection = ({ - {item.name} + {item.name} {item.period} {item.description} - {item.website.url && ( - - {item.website.label || item.website.url} - - )} + ))} @@ -610,17 +618,13 @@ const AwardsSection = ({ {items.map((item) => ( - {item.title} + {item.title} {item.awarder} {item.date} {item.description} - {item.website.url && ( - - {item.website.label || item.website.url} - - )} + ))} @@ -647,17 +651,13 @@ const CertificationsSection = ({ {items.map((item) => ( - {item.title} + {item.title} {item.issuer} {item.date} {item.description} - {item.website.url && ( - - {item.website.label || item.website.url} - - )} + ))} @@ -684,17 +684,13 @@ const PublicationsSection = ({ {items.map((item) => ( - {item.title} + {item.title} {item.publisher} {item.date} {item.description} - {item.website.url && ( - - {item.website.label || item.website.url} - - )} + ))} @@ -727,13 +723,13 @@ const VolunteerSection = ({ {inlineItemHeader ? ( {item.location}} - middle={{item.organization}} + middle={{item.organization}} trailing={{item.period}} /> ) : ( <> - {item.organization} + {item.organization} {item.location} @@ -743,11 +739,7 @@ const VolunteerSection = ({ {item.description} - {item.website.url && ( - - {item.website.label || item.website.url} - - )} + ))} @@ -774,17 +766,13 @@ const ReferencesSection = ({ {items.map((item) => ( - {item.name} + {item.name} {item.position} {[item.phone]} {item.description} - {item.website.url && ( - - {item.website.label || item.website.url} - - )} + ))} diff --git a/packages/ui/src/styles/globals.css b/packages/ui/src/styles/globals.css index 3744ddafb..55c60daff 100644 --- a/packages/ui/src/styles/globals.css +++ b/packages/ui/src/styles/globals.css @@ -152,6 +152,10 @@ [role="button"]:not(:disabled) { cursor: pointer; } + + .wysiwyg { + @apply prose prose-sm prose-zinc dark:prose-invert max-w-none prose-p:my-0! prose-li:my-0! prose-ul:my-0! prose-ol:my-0!; + } } @media (prefers-reduced-motion: reduce) {