fix(#2978): implement inlineLink functionality

This commit is contained in:
Amruth Pillai
2026-05-07 21:47:00 +02:00
parent 891ce60270
commit d5891ff035
5 changed files with 99 additions and 56 deletions
+1 -2
View File
@@ -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]",
@@ -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);
});
});
@@ -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);
};
+42 -54
View File
@@ -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 <View style={composeStyles(sectionItemHeaderStyle)}>{children}</View>;
};
type ItemWebsite = {
url: string;
label: string;
inlineLink?: boolean | undefined;
};
const ItemTitle = ({ children, website }: { children: ReactNode; website: ItemWebsite }) => {
const inlineWebsiteUrl = getInlineItemWebsiteUrl(website);
const title = <Bold>{children}</Bold>;
if (!inlineWebsiteUrl) return title;
return <Link src={inlineWebsiteUrl}>{title}</Link>;
};
const ItemWebsiteLink = ({ website }: { website: ItemWebsite }) => {
if (!shouldRenderSeparateItemWebsite(website)) return null;
return <Link src={website.url}>{website.label || website.url}</Link>;
};
const SummarySection = ({ showHeading = true }: { showHeading?: boolean } = {}) => {
const data = useRender();
const { summary } = data;
@@ -272,9 +294,7 @@ const ProfileSection = ({
<Bold>{item.network}</Bold>
</View>
</SectionItemHeader>
<Link src={item.website.url}>
<Text>{item.username}</Text>
</Link>
<Link src={item.website.url}>{item.username}</Link>
</SectionItem>
))}
</SectionItems>
@@ -316,7 +336,7 @@ const ExperienceSection = ({
</Text>
) : null
}
middle={<Bold>{item.company}</Bold>}
middle={<ItemTitle website={item.website}>{item.company}</ItemTitle>}
trailing={<Text style={composeStyles(alignRightStyle)}>{item.period}</Text>}
/>
);
@@ -324,7 +344,7 @@ const ExperienceSection = ({
const renderSplitHeader = () => (
<>
<View style={composeStyles(splitRowStyle)}>
<Bold>{item.company}</Bold>
<ItemTitle website={item.website}>{item.company}</ItemTitle>
<Text style={composeStyles(alignRightStyle)}>{item.location}</Text>
</View>
@@ -355,11 +375,7 @@ const ExperienceSection = ({
{item.roles.length === 0 && <RichText>{item.description}</RichText>}
{item.website.url && (
<Link src={item.website.url}>
<Small>{item.website.label || item.website.url}</Small>
</Link>
)}
<ItemWebsiteLink website={item.website} />
</SectionItem>
);
})}
@@ -406,7 +422,7 @@ const EducationSection = ({
</Text>
) : null
}
middle={<Bold>{item.school}</Bold>}
middle={<ItemTitle website={item.website}>{item.school}</ItemTitle>}
trailing={<Text style={composeStyles(alignRightStyle)}>{item.period}</Text>}
/>
{gradeAndLocation && <Text>{gradeAndLocation}</Text>}
@@ -416,7 +432,7 @@ const EducationSection = ({
const renderSplitHeader = () => (
<>
<View style={composeStyles(splitRowStyle)}>
<Bold>{item.school}</Bold>
<ItemTitle website={item.website}>{item.school}</ItemTitle>
<Text style={composeStyles(alignRightStyle)}>{degreeAndGrade}</Text>
</View>
@@ -433,11 +449,7 @@ const EducationSection = ({
<RichText>{item.description}</RichText>
{item.website.url && (
<Link src={item.website.url}>
<Small>{item.website.label || item.website.url}</Small>
</Link>
)}
<ItemWebsiteLink website={item.website} />
</SectionItem>
);
})}
@@ -468,18 +480,14 @@ const ProjectsSection = ({
<SectionItem key={item.id}>
<SectionItemHeader>
<View style={composeStyles(splitRowStyle)}>
<Bold>{item.name}</Bold>
<ItemTitle website={item.website}>{item.name}</ItemTitle>
<Text style={composeStyles(alignRightStyle)}>{item.period}</Text>
</View>
</SectionItemHeader>
<RichText>{item.description}</RichText>
{item.website.url && (
<Link src={item.website.url}>
<Small>{item.website.label || item.website.url}</Small>
</Link>
)}
<ItemWebsiteLink website={item.website} />
</SectionItem>
))}
</SectionItems>
@@ -610,17 +618,13 @@ const AwardsSection = ({
{items.map((item) => (
<SectionItem key={item.id}>
<SectionItemHeader>
<Bold>{item.title}</Bold>
<ItemTitle website={item.website}>{item.title}</ItemTitle>
<Text>{item.awarder}</Text>
<Small>{item.date}</Small>
</SectionItemHeader>
<RichText>{item.description}</RichText>
{item.website.url && (
<Link src={item.website.url}>
<Small>{item.website.label || item.website.url}</Small>
</Link>
)}
<ItemWebsiteLink website={item.website} />
</SectionItem>
))}
</SectionItems>
@@ -647,17 +651,13 @@ const CertificationsSection = ({
{items.map((item) => (
<SectionItem key={item.id}>
<SectionItemHeader>
<Bold>{item.title}</Bold>
<ItemTitle website={item.website}>{item.title}</ItemTitle>
<Text>{item.issuer}</Text>
<Small>{item.date}</Small>
</SectionItemHeader>
<RichText>{item.description}</RichText>
{item.website.url && (
<Link src={item.website.url}>
<Small>{item.website.label || item.website.url}</Small>
</Link>
)}
<ItemWebsiteLink website={item.website} />
</SectionItem>
))}
</SectionItems>
@@ -684,17 +684,13 @@ const PublicationsSection = ({
{items.map((item) => (
<SectionItem key={item.id}>
<SectionItemHeader>
<Bold>{item.title}</Bold>
<ItemTitle website={item.website}>{item.title}</ItemTitle>
<Text>{item.publisher}</Text>
<Small>{item.date}</Small>
</SectionItemHeader>
<RichText>{item.description}</RichText>
{item.website.url && (
<Link src={item.website.url}>
<Small>{item.website.label || item.website.url}</Small>
</Link>
)}
<ItemWebsiteLink website={item.website} />
</SectionItem>
))}
</SectionItems>
@@ -727,13 +723,13 @@ const VolunteerSection = ({
{inlineItemHeader ? (
<InlineItemHeader
leading={<Text>{item.location}</Text>}
middle={<Bold>{item.organization}</Bold>}
middle={<ItemTitle website={item.website}>{item.organization}</ItemTitle>}
trailing={<Text style={composeStyles(alignRightStyle)}>{item.period}</Text>}
/>
) : (
<>
<View style={composeStyles(splitRowStyle)}>
<Bold>{item.organization}</Bold>
<ItemTitle website={item.website}>{item.organization}</ItemTitle>
<Text style={composeStyles(alignRightStyle)}>{item.location}</Text>
</View>
@@ -743,11 +739,7 @@ const VolunteerSection = ({
</SectionItemHeader>
<RichText>{item.description}</RichText>
{item.website.url && (
<Link src={item.website.url}>
<Small>{item.website.label || item.website.url}</Small>
</Link>
)}
<ItemWebsiteLink website={item.website} />
</SectionItem>
))}
</SectionItems>
@@ -774,17 +766,13 @@ const ReferencesSection = ({
{items.map((item) => (
<SectionItem key={item.id}>
<SectionItemHeader>
<Bold>{item.name}</Bold>
<ItemTitle website={item.website}>{item.name}</ItemTitle>
<Text>{item.position}</Text>
<MetaLine>{[item.phone]}</MetaLine>
</SectionItemHeader>
<RichText>{item.description}</RichText>
{item.website.url && (
<Link src={item.website.url}>
<Small>{item.website.label || item.website.url}</Small>
</Link>
)}
<ItemWebsiteLink website={item.website} />
</SectionItem>
))}
</SectionItems>
+4
View File
@@ -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) {