feat: separate links option at section level to reduce section item height

This commit is contained in:
Jonathan Zaehringer
2024-03-20 22:20:56 +01:00
parent b23efa773f
commit 6f2e75f22b
15 changed files with 547 additions and 148 deletions

View File

@ -117,16 +117,17 @@ const Rating = ({ level }: RatingProps) => (
type LinkProps = {
url: URL;
icon?: React.ReactNode;
iconOnRight?: boolean;
label?: string;
className?: string;
};
const Link = ({ url, icon, label, className }: LinkProps) => {
const Link = ({ url, icon, iconOnRight, label, className }: LinkProps) => {
if (!isUrl(url.href)) return null;
return (
<div className="flex items-center gap-x-1.5">
{icon ?? <i className="ph ph-bold ph-link text-primary" />}
{!iconOnRight && ( icon ?? <i className="ph ph-bold ph-link text-primary" />)}
<a
href={url.href}
target="_blank"
@ -135,10 +136,40 @@ const Link = ({ url, icon, label, className }: LinkProps) => {
>
{label || url.label || url.href}
</a>
{iconOnRight && ( icon ?? <i className="ph ph-bold ph-link text-primary" />)}
</div>
);
};
type LinkedEntityProps = {
name: string;
url: URL;
separateLinks: boolean;
className?: string;
};
const LinkedEntity = ({ name, url, separateLinks, className}: LinkedEntityProps) => {
if (!separateLinks && isUrl(url.href)) {
return (
<Link
url={url}
label={name}
icon={
<i className="ph ph-bold ph-globe text-primary" />
}
iconOnRight={true}
className={className}
/>
);
} else {
return (
<div className={className}>
{name}
</div>
);
}
};
type SectionProps<T> = {
section: SectionWithItem<T> | CustomSectionGroup;
children?: (item: T) => React.ReactNode;
@ -205,7 +236,7 @@ const Section = <T,>({
<p className="text-sm">{keywords.join(", ")}</p>
)}
{url !== undefined && <Link url={url} />}
{url !== undefined && section.separateLinks && <Link url={url} />}
<div className="absolute left-[-4.5px] top-px hidden size-[8px] rounded-full bg-primary group-[.main]:block" />
</div>
@ -255,7 +286,7 @@ const Experience = () => {
<Section<Experience> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.company}</div>
<LinkedEntity name={item.company} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.position}</div>
<div>{item.location}</div>
<div className="font-bold">{item.date}</div>
@ -272,7 +303,7 @@ const Education = () => {
<Section<Education> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.institution}</div>
<LinkedEntity name={item.institution} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.area}</div>
<div>{item.score}</div>
<div>{item.studyType}</div>
@ -291,7 +322,7 @@ const Awards = () => {
{(item) => (
<div>
<div className="font-bold">{item.title}</div>
<div>{item.awarder}</div>
<LinkedEntity name={item.awarder} url={item.url} separateLinks={section.separateLinks}/>
<div className="font-bold">{item.date}</div>
</div>
)}
@ -303,11 +334,11 @@ const Certifications = () => {
const section = useArtboardStore((state) => state.resume.sections.certifications);
return (
<Section<Certification> section={section} urlKey="url" summaryKey="summary">
<Section<Certification> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<div>{item.issuer}</div>
<LinkedEntity name={item.issuer} url={item.url} separateLinks={section.separateLinks}/>
<div className="font-bold">{item.date}</div>
</div>
)}
@ -347,7 +378,7 @@ const Publications = () => {
<Section<Publication> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.publisher}</div>
<div className="font-bold">{item.date}</div>
</div>
@ -363,7 +394,7 @@ const Volunteer = () => {
<Section<Volunteer> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.organization}</div>
<LinkedEntity name={item.organization} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.position}</div>
<div>{item.location}</div>
<div className="font-bold">{item.date}</div>
@ -396,7 +427,7 @@ const Projects = () => {
{(item) => (
<div>
<div>
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
<div className="font-bold">{item.date}</div>
@ -414,7 +445,7 @@ const References = () => {
<Section<Reference> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
</div>
)}
@ -435,7 +466,7 @@ const Custom = ({ id }: { id: string }) => {
{(item) => (
<div>
<div>
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
<div className="font-bold">{item.date}</div>

View File

@ -7,6 +7,7 @@ import {
Experience,
Interest,
Language,
Metadata,
Profile,
Project,
Publication,
@ -108,16 +109,17 @@ const Rating = ({ level }: RatingProps) => (
type LinkProps = {
url: URL;
icon?: React.ReactNode;
iconOnRight?: boolean;
label?: string;
className?: string;
};
const Link = ({ url, icon, label, className }: LinkProps) => {
const Link = ({ url, icon, iconOnRight, label, className }: LinkProps) => {
if (!isUrl(url.href)) return null;
return (
<div className="flex items-center gap-x-1.5">
{icon ?? <i className="ph ph-bold ph-link text-primary" />}
{!iconOnRight && (icon ?? <i className="ph ph-bold ph-link text-primary" />)}
<a
href={url.href}
target="_blank"
@ -126,10 +128,40 @@ const Link = ({ url, icon, label, className }: LinkProps) => {
>
{label || url.label || url.href}
</a>
{iconOnRight && (icon ?? <i className="ph ph-bold ph-link text-primary" />)}
</div>
);
};
type LinkedEntityProps = {
name: string;
url: URL;
separateLinks: boolean;
className?: string;
};
const LinkedEntity = ({ name, url, separateLinks, className}: LinkedEntityProps) => {
if (!separateLinks && isUrl(url.href)) {
return (
<Link
url={url}
label={name}
icon={
<i className="ph ph-bold ph-globe text-primary" />
}
iconOnRight={true}
className={className}
/>
);
} else {
return (
<div className={className}>
{name}
</div>
);
}
};
type SectionProps<T> = {
section: SectionWithItem<T> | CustomSectionGroup;
children?: (item: T) => React.ReactNode;
@ -173,7 +205,7 @@ const Section = <T,>({
<div key={item.id} className={cn("space-y-2", className)}>
<div>
{children?.(item as T)}
{url !== undefined && <Link url={url} />}
{url !== undefined && section.separateLinks && <Link url={url} />}
</div>
{summary !== undefined && !isEmptyString(summary) && (
@ -233,7 +265,7 @@ const Experience = () => {
{(item) => (
<div className="flex items-start justify-between">
<div className="text-left">
<div className="font-bold">{item.company}</div>
<LinkedEntity name={item.company} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.position}</div>
</div>
@ -255,7 +287,7 @@ const Education = () => {
{(item) => (
<div className="flex items-start justify-between">
<div className="text-left">
<div className="font-bold">{item.institution}</div>
<LinkedEntity name={item.institution} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.area}</div>
<div>{item.score}</div>
</div>
@ -279,7 +311,7 @@ const Awards = () => {
<div className="flex items-start justify-between">
<div className="text-left">
<div className="font-bold">{item.title}</div>
<div>{item.awarder}</div>
<LinkedEntity name={item.awarder} url={item.url} separateLinks={section.separateLinks}/>
</div>
<div className="shrink-0 text-right">
@ -300,7 +332,7 @@ const Certifications = () => {
<div className="flex items-start justify-between">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<div>{item.issuer}</div>
<LinkedEntity name={item.issuer} url={item.url} separateLinks={section.separateLinks}/>
</div>
<div className="shrink-0 text-right">
@ -345,7 +377,7 @@ const Publications = () => {
{(item) => (
<div className="flex items-start justify-between">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.publisher}</div>
</div>
@ -366,7 +398,7 @@ const Volunteer = () => {
{(item) => (
<div className="flex items-start justify-between">
<div className="text-left">
<div className="font-bold">{item.organization}</div>
<LinkedEntity name={item.organization} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.position}</div>
</div>
@ -403,7 +435,7 @@ const Projects = () => {
{(item) => (
<div className="flex items-start justify-between">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
</div>
@ -423,7 +455,7 @@ const References = () => {
<Section<Reference> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
</div>
)}
@ -444,7 +476,7 @@ const Custom = ({ id }: { id: string }) => {
{(item) => (
<div className="flex items-start justify-between">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
</div>

View File

@ -111,16 +111,17 @@ const Rating = ({ level }: RatingProps) => (
type LinkProps = {
url: URL;
icon?: React.ReactNode;
iconOnRight?: boolean;
label?: string;
className?: string;
};
const Link = ({ url, icon, label, className }: LinkProps) => {
const Link = ({ url, icon, iconOnRight, label, className }: LinkProps) => {
if (!isUrl(url.href)) return null;
return (
<div className="flex items-center gap-x-1.5">
{icon ?? <i className="ph ph-bold ph-link text-primary" />}
{!iconOnRight && (icon ?? <i className="ph ph-bold ph-link text-primary" />)}
<a
href={url.href}
target="_blank"
@ -129,10 +130,40 @@ const Link = ({ url, icon, label, className }: LinkProps) => {
>
{label || url.label || url.href}
</a>
{iconOnRight && (icon ?? <i className="ph ph-bold ph-link text-primary" />)}
</div>
);
};
type LinkedEntityProps = {
name: string;
url: URL;
separateLinks: boolean;
className?: string;
};
const LinkedEntity = ({ name, url, separateLinks, className}: LinkedEntityProps) => {
if (!separateLinks && isUrl(url.href)) {
return (
<Link
url={url}
label={name}
icon={
<i className="ph ph-bold ph-globe text-primary" />
}
iconOnRight={true}
className={className}
/>
);
} else {
return (
<div className={className}>
{name}
</div>
);
}
};
type SectionProps<T> = {
section: SectionWithItem<T> | CustomSectionGroup;
children?: (item: T) => React.ReactNode;
@ -174,7 +205,7 @@ const Section = <T,>({
<div key={item.id} className={cn("space-y-2", className)}>
<div>
{children?.(item as T)}
{url !== undefined && <Link url={url} />}
{url !== undefined && section.separateLinks && <Link url={url} />}
</div>
{summary !== undefined && !isEmptyString(summary) && (
@ -202,7 +233,7 @@ const Experience = () => {
{(item) => (
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.company}</div>
<LinkedEntity name={item.company} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.position}</div>
</div>
@ -224,7 +255,7 @@ const Education = () => {
{(item) => (
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.institution}</div>
<LinkedEntity name={item.institution} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.area}</div>
<div>{item.score}</div>
</div>
@ -280,7 +311,7 @@ const Awards = () => {
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.title}</div>
<div>{item.awarder}</div>
<LinkedEntity name={item.awarder} url={item.url} separateLinks={section.separateLinks}/>
</div>
<div className="shrink-0 text-right">
@ -302,6 +333,7 @@ const Certifications = () => {
<div className="text-left">
<div className="font-bold">{item.name}</div>
<div>{item.issuer}</div>
<LinkedEntity name={item.issuer} url={item.url} separateLinks={section.separateLinks}/>
</div>
<div className="shrink-0 text-right">
@ -346,7 +378,7 @@ const Publications = () => {
{(item) => (
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.publisher}</div>
</div>
@ -367,7 +399,7 @@ const Volunteer = () => {
{(item) => (
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.organization}</div>
<LinkedEntity name={item.organization} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.position}</div>
</div>
@ -404,7 +436,7 @@ const Projects = () => {
{(item) => (
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
</div>
@ -424,7 +456,7 @@ const References = () => {
<Section<Reference> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
</div>
)}
@ -445,7 +477,7 @@ const Custom = ({ id }: { id: string }) => {
{(item) => (
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
</div>

View File

@ -128,16 +128,17 @@ const Rating = ({ level }: RatingProps) => (
type LinkProps = {
url: URL;
icon?: React.ReactNode;
iconOnRight?: boolean;
label?: string;
className?: string;
};
const Link = ({ url, icon, label, className }: LinkProps) => {
const Link = ({ url, icon, iconOnRight, label, className }: LinkProps) => {
if (!isUrl(url.href)) return null;
return (
<div className="flex items-center gap-x-1.5">
{icon ?? <i className="ph ph-bold ph-link text-primary" />}
{!iconOnRight && (icon ?? <i className="ph ph-bold ph-link text-primary" />)}
<a
href={url.href}
target="_blank"
@ -146,10 +147,40 @@ const Link = ({ url, icon, label, className }: LinkProps) => {
>
{label || url.label || url.href}
</a>
{iconOnRight && (icon ?? <i className="ph ph-bold ph-link text-primary" />)}
</div>
);
};
type LinkedEntityProps = {
name: string;
url: URL;
separateLinks: boolean;
className?: string;
};
const LinkedEntity = ({ name, url, separateLinks, className}: LinkedEntityProps) => {
if (!separateLinks && isUrl(url.href)) {
return (
<Link
url={url}
label={name}
icon={
<i className="ph ph-bold ph-globe text-primary" />
}
iconOnRight={true}
className={className}
/>
);
} else {
return (
<div className={className}>
{name}
</div>
);
}
};
type SectionProps<T> = {
section: SectionWithItem<T> | CustomSectionGroup;
children?: (item: T) => React.ReactNode;
@ -195,7 +226,7 @@ const Section = <T,>({
<div className="relative -ml-4 group-[.sidebar]:ml-0">
<div className="pl-4 group-[.sidebar]:pl-0">
{children?.(item as T)}
{url !== undefined && <Link url={url} />}
{url !== undefined && section.separateLinks && <Link url={url} />}
</div>
<div className="absolute inset-y-0 -left-px border-l-[4px] border-primary group-[.sidebar]:hidden" />
@ -260,7 +291,7 @@ const Experience = () => {
{(item) => (
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.company}</div>
<LinkedEntity name={item.company} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.position}</div>
</div>
@ -282,7 +313,7 @@ const Education = () => {
{(item) => (
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.institution}</div>
<LinkedEntity name={item.institution} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.area}</div>
<div>{item.score}</div>
</div>
@ -306,7 +337,7 @@ const Awards = () => {
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.title}</div>
<div>{item.awarder}</div>
<LinkedEntity name={item.awarder} url={item.url} separateLinks={section.separateLinks}/>
</div>
<div className="shrink-0 text-right">
@ -327,7 +358,7 @@ const Certifications = () => {
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<div>{item.issuer}</div>
<LinkedEntity name={item.issuer} url={item.url} separateLinks={section.separateLinks}/>
</div>
<div className="shrink-0 text-right">
@ -372,7 +403,7 @@ const Publications = () => {
{(item) => (
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.publisher}</div>
</div>
@ -393,7 +424,7 @@ const Volunteer = () => {
{(item) => (
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.organization}</div>
<LinkedEntity name={item.organization} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.position}</div>
</div>
@ -430,7 +461,7 @@ const Projects = () => {
{(item) => (
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
</div>
@ -450,7 +481,7 @@ const References = () => {
<Section<Reference> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
</div>
)}
@ -471,7 +502,7 @@ const Custom = ({ id }: { id: string }) => {
{(item) => (
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
</div>

View File

@ -106,16 +106,17 @@ const Rating = ({ level }: RatingProps) => (
type LinkProps = {
url: URL;
icon?: React.ReactNode;
iconOnRight?: boolean;
label?: string;
className?: string;
};
const Link = ({ url, icon, label, className }: LinkProps) => {
const Link = ({ url, icon, iconOnRight, label, className }: LinkProps) => {
if (!isUrl(url.href)) return null;
return (
<div className="flex items-center gap-x-1.5">
{icon ?? <i className="ph ph-bold ph-link text-primary group-[.sidebar]:text-background" />}
{!iconOnRight && (icon ?? <i className="ph ph-bold ph-link text-primary group-[.sidebar]:text-background" />)}
<a
href={url.href}
target="_blank"
@ -124,10 +125,40 @@ const Link = ({ url, icon, label, className }: LinkProps) => {
>
{label || url.label || url.href}
</a>
{iconOnRight && (icon ?? <i className="ph ph-bold ph-link text-primary group-[.sidebar]:text-background" />)}
</div>
);
};
type LinkedEntityProps = {
name: string;
url: URL;
separateLinks: boolean;
className?: string;
};
const LinkedEntity = ({ name, url, separateLinks, className}: LinkedEntityProps) => {
if (!separateLinks && isUrl(url.href)) {
return (
<Link
url={url}
label={name}
icon={
<i className="ph ph-bold ph-globe text-primary group-[.sidebar]:text-primary" />
}
iconOnRight={true}
className={className}
/>
);
} else {
return (
<div className={className}>
{name}
</div>
);
}
};
type SectionProps<T> = {
section: SectionWithItem<T> | CustomSectionGroup;
children?: (item: T) => React.ReactNode;
@ -169,7 +200,7 @@ const Section = <T,>({
<div key={item.id} className={cn("space-y-2", className)}>
<div>
{children?.(item as T)}
{url !== undefined && <Link url={url} />}
{url !== undefined && section.separateLinks && <Link url={url} />}
</div>
{summary !== undefined && !isEmptyString(summary) && (
@ -229,7 +260,7 @@ const Experience = () => {
{(item) => (
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.company}</div>
<LinkedEntity name={item.company} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.position}</div>
</div>
@ -251,7 +282,7 @@ const Education = () => {
{(item) => (
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.institution}</div>
<LinkedEntity name={item.institution} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.area}</div>
<div>{item.score}</div>
</div>
@ -275,7 +306,7 @@ const Awards = () => {
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.title}</div>
<div>{item.awarder}</div>
<LinkedEntity name={item.awarder} url={item.url} separateLinks={section.separateLinks}/>
</div>
<div className="shrink-0 text-right group-[.sidebar]:text-left">
@ -296,7 +327,7 @@ const Certifications = () => {
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<div>{item.issuer}</div>
<LinkedEntity name={item.issuer} url={item.url} separateLinks={section.separateLinks}/>
</div>
<div className="shrink-0 text-right group-[.sidebar]:text-left">
@ -341,7 +372,7 @@ const Publications = () => {
{(item) => (
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.publisher}</div>
</div>
@ -362,7 +393,7 @@ const Volunteer = () => {
{(item) => (
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.organization}</div>
<LinkedEntity name={item.organization} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.position}</div>
</div>
@ -399,7 +430,7 @@ const Projects = () => {
{(item) => (
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
</div>
@ -419,7 +450,7 @@ const References = () => {
<Section<Reference> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
</div>
)}
@ -440,7 +471,7 @@ const Custom = ({ id }: { id: string }) => {
{(item) => (
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
</div>

View File

@ -114,16 +114,17 @@ const Rating = ({ level }: RatingProps) => {
type LinkProps = {
url: URL;
icon?: React.ReactNode;
iconOnRight?: boolean;
label?: string;
className?: string;
};
const Link = ({ url, icon, label, className }: LinkProps) => {
const Link = ({ url, icon, iconOnRight, label, className }: LinkProps) => {
if (!isUrl(url.href)) return null;
return (
<div className="flex items-center gap-x-1.5">
{icon ?? <i className="ph ph-bold ph-link text-primary group-[.sidebar]:text-primary" />}
{!iconOnRight && (icon ?? <i className="ph ph-bold ph-link text-primary group-[.sidebar]:text-primary" />)}
<a
href={url.href}
target="_blank"
@ -132,10 +133,40 @@ const Link = ({ url, icon, label, className }: LinkProps) => {
>
{label || url.label || url.href}
</a>
{iconOnRight && (icon ?? <i className="ph ph-bold ph-link text-primary group-[.sidebar]:text-primary" />)}
</div>
);
};
type LinkedEntityProps = {
name: string;
url: URL;
separateLinks: boolean;
className?: string;
};
const LinkedEntity = ({ name, url, separateLinks, className}: LinkedEntityProps) => {
if (!separateLinks && isUrl(url.href)) {
return (
<Link
url={url}
label={name}
icon={
<i className="ph ph-bold ph-globe text-primary group-[.sidebar]:text-primary" />
}
iconOnRight={true}
className={className}
/>
);
} else {
return (
<div className={className}>
{name}
</div>
);
}
};
type SectionProps<T> = {
section: SectionWithItem<T> | CustomSectionGroup;
children?: (item: T) => React.ReactNode;
@ -179,7 +210,7 @@ const Section = <T,>({
<div key={item.id} className={cn("space-y-2", className)}>
<div>
{children?.(item as T)}
{url !== undefined && <Link url={url} />}
{url !== undefined && section.separateLinks && <Link url={url} />}
</div>
{summary !== undefined && !isEmptyString(summary) && (
@ -207,7 +238,7 @@ const Experience = () => {
{(item) => (
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.company}</div>
<LinkedEntity name={item.company} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.position}</div>
</div>
@ -229,7 +260,7 @@ const Education = () => {
{(item) => (
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.institution}</div>
<LinkedEntity name={item.institution} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.area}</div>
<div>{item.score}</div>
</div>
@ -285,7 +316,7 @@ const Awards = () => {
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.title}</div>
<div>{item.awarder}</div>
<LinkedEntity name={item.awarder} url={item.url} separateLinks={section.separateLinks}/>
</div>
<div className="shrink-0 text-right">
@ -306,7 +337,7 @@ const Certifications = () => {
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<div>{item.issuer}</div>
<LinkedEntity name={item.issuer} url={item.url} separateLinks={section.separateLinks}/>
</div>
<div className="shrink-0 text-right">
@ -351,7 +382,7 @@ const Publications = () => {
{(item) => (
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.publisher}</div>
</div>
@ -372,7 +403,7 @@ const Volunteer = () => {
{(item) => (
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.organization}</div>
<LinkedEntity name={item.organization} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.position}</div>
</div>
@ -409,7 +440,7 @@ const Projects = () => {
{(item) => (
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
</div>
@ -429,7 +460,7 @@ const References = () => {
<Section<Reference> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
</div>
)}
@ -450,7 +481,7 @@ const Custom = ({ id }: { id: string }) => {
{(item) => (
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
</div>

View File

@ -134,16 +134,17 @@ const Rating = ({ level }: RatingProps) => (
type LinkProps = {
url: URL;
icon?: React.ReactNode;
iconOnRight?: boolean;
label?: string;
className?: string;
};
const Link = ({ url, icon, label, className }: LinkProps) => {
const Link = ({ url, icon, iconOnRight, label, className }: LinkProps) => {
if (!isUrl(url.href)) return null;
return (
<div className="flex items-center gap-x-1.5">
{icon ?? <i className="ph ph-bold ph-link text-primary" />}
{!iconOnRight && (icon ?? <i className="ph ph-bold ph-link text-primary" />)}
<a
href={url.href}
target="_blank"
@ -152,10 +153,40 @@ const Link = ({ url, icon, label, className }: LinkProps) => {
>
{label || url.label || url.href}
</a>
{iconOnRight && (icon ?? <i className="ph ph-bold ph-link text-primary" />)}
</div>
);
};
type LinkedEntityProps = {
name: string;
url: URL;
separateLinks: boolean;
className?: string;
};
const LinkedEntity = ({ name, url, separateLinks, className}: LinkedEntityProps) => {
if (!separateLinks && isUrl(url.href)) {
return (
<Link
url={url}
label={name}
icon={
<i className="ph ph-bold ph-globe text-primary" />
}
iconOnRight={true}
className={className}
/>
);
} else {
return (
<div className={className}>
{name}
</div>
);
}
};
type SectionProps<T> = {
section: SectionWithItem<T> | CustomSectionGroup;
children?: (item: T) => React.ReactNode;
@ -209,7 +240,7 @@ const Section = <T,>({
<p className="text-sm">{keywords.join(", ")}</p>
)}
{url !== undefined && <Link url={url} />}
{url !== undefined && section.separateLinks && <Link url={url} />}
</div>
);
})}
@ -225,7 +256,7 @@ const Experience = () => {
<Section<Experience> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.company}</div>
<LinkedEntity name={item.company} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.position}</div>
<div>{item.location}</div>
<div className="font-bold">{item.date}</div>
@ -242,7 +273,7 @@ const Education = () => {
<Section<Education> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.institution}</div>
<LinkedEntity name={item.institution} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.area}</div>
<div>{item.score}</div>
<div>{item.studyType}</div>
@ -261,7 +292,7 @@ const Awards = () => {
{(item) => (
<div>
<div className="font-bold">{item.title}</div>
<div>{item.awarder}</div>
<LinkedEntity name={item.awarder} url={item.url} separateLinks={section.separateLinks}/>
<div className="font-bold">{item.date}</div>
</div>
)}
@ -277,7 +308,7 @@ const Certifications = () => {
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<div>{item.issuer}</div>
<LinkedEntity name={item.issuer} url={item.url} separateLinks={section.separateLinks}/>
<div className="font-bold">{item.date}</div>
</div>
)}
@ -317,7 +348,7 @@ const Publications = () => {
<Section<Publication> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.publisher}</div>
<div className="font-bold">{item.date}</div>
</div>
@ -333,7 +364,7 @@ const Volunteer = () => {
<Section<Volunteer> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.organization}</div>
<LinkedEntity name={item.organization} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.position}</div>
<div>{item.location}</div>
<div className="font-bold">{item.date}</div>
@ -366,7 +397,7 @@ const Projects = () => {
{(item) => (
<div>
<div>
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
<div className="font-bold">{item.date}</div>
</div>
@ -383,7 +414,7 @@ const References = () => {
<Section<Reference> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
</div>
)}
@ -404,7 +435,7 @@ const Custom = ({ id }: { id: string }) => {
{(item) => (
<div>
<div>
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
<div>{item.location}</div>
<div className="font-bold">{item.date}</div>

View File

@ -131,16 +131,17 @@ const Rating = ({ level }: RatingProps) => (
type LinkProps = {
url: URL;
icon?: React.ReactNode;
iconOnRight?: boolean;
label?: string;
className?: string;
};
const Link = ({ url, icon, label, className }: LinkProps) => {
const Link = ({ url, icon, iconOnRight, label, className }: LinkProps) => {
if (!isUrl(url.href)) return null;
return (
<div className="flex items-center gap-x-1.5">
{icon ?? <i className="ph ph-bold ph-link text-primary" />}
{!iconOnRight && ( icon ?? <i className="ph ph-bold ph-link text-primary" />)}
<a
href={url.href}
target="_blank"
@ -149,10 +150,40 @@ const Link = ({ url, icon, label, className }: LinkProps) => {
>
{label || url.label || url.href}
</a>
{iconOnRight && ( icon ?? <i className="ph ph-bold ph-link text-primary" />)}
</div>
);
};
type LinkedEntityProps = {
name: string;
url: URL;
separateLinks: boolean;
className?: string;
};
const LinkedEntity = ({ name, url, separateLinks, className}: LinkedEntityProps) => {
if (!separateLinks && isUrl(url.href)) {
return (
<Link
url={url}
label={name}
icon={
<i className="ph ph-bold ph-globe text-primary" />
}
iconOnRight={true}
className={className}
/>
);
} else {
return (
<div className={className}>
{name}
</div>
);
}
};
type SectionProps<T> = {
section: SectionWithItem<T> | CustomSectionGroup;
children?: (item: T) => React.ReactNode;
@ -206,7 +237,7 @@ const Section = <T,>({
<p className="text-sm">{keywords.join(", ")}</p>
)}
{url !== undefined && <Link url={url} />}
{url !== undefined && section.separateLinks && <Link url={url} />}
</div>
);
})}
@ -222,7 +253,7 @@ const Experience = () => {
<Section<Experience> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.company}</div>
<LinkedEntity name={item.company} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.position}</div>
<div>{item.location}</div>
<div className="font-bold">{item.date}</div>
@ -239,7 +270,7 @@ const Education = () => {
<Section<Education> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.institution}</div>
<LinkedEntity name={item.institution} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.area}</div>
<div>{item.score}</div>
<div>{item.studyType}</div>
@ -258,7 +289,7 @@ const Awards = () => {
{(item) => (
<div>
<div className="font-bold">{item.title}</div>
<div>{item.awarder}</div>
<LinkedEntity name={item.awarder} url={item.url} separateLinks={section.separateLinks}/>
<div className="font-bold">{item.date}</div>
</div>
)}
@ -274,7 +305,7 @@ const Certifications = () => {
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<div>{item.issuer}</div>
<LinkedEntity name={item.issuer} url={item.url} separateLinks={section.separateLinks}/>
<div className="font-bold">{item.date}</div>
</div>
)}
@ -314,7 +345,7 @@ const Publications = () => {
<Section<Publication> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.publisher}</div>
<div className="font-bold">{item.date}</div>
</div>
@ -330,7 +361,7 @@ const Volunteer = () => {
<Section<Volunteer> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.organization}</div>
<LinkedEntity name={item.organization} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.position}</div>
<div>{item.location}</div>
<div className="font-bold">{item.date}</div>
@ -363,7 +394,7 @@ const Projects = () => {
{(item) => (
<div>
<div>
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
<div className="font-bold">{item.date}</div>
</div>
@ -380,7 +411,7 @@ const References = () => {
<Section<Reference> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
</div>
)}
@ -401,7 +432,7 @@ const Custom = ({ id }: { id: string }) => {
{(item) => (
<div>
<div>
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
<div>{item.location}</div>
<div className="font-bold">{item.date}</div>

View File

@ -110,16 +110,17 @@ const Summary = () => {
type LinkProps = {
url: URL;
icon?: React.ReactNode;
iconOnRight?: boolean;
label?: string;
className?: string;
};
const Link = ({ url, icon, label, className }: LinkProps) => {
const Link = ({ url, icon, iconOnRight, label, className }: LinkProps) => {
if (!isUrl(url.href)) return null;
return (
<div className="flex items-center gap-x-1.5">
{icon ?? <i className="ph ph-bold ph-link text-primary" />}
{!iconOnRight && (icon ?? <i className="ph ph-bold ph-link text-primary" />)}
<a
href={url.href}
target="_blank"
@ -128,10 +129,40 @@ const Link = ({ url, icon, label, className }: LinkProps) => {
>
{label || url.label || url.href}
</a>
{iconOnRight && (icon ?? <i className="ph ph-bold ph-link text-primary" />)}
</div>
);
};
type LinkedEntityProps = {
name: string;
url: URL;
separateLinks: boolean;
className?: string;
};
const LinkedEntity = ({ name, url, separateLinks, className}: LinkedEntityProps) => {
if (!separateLinks && isUrl(url.href)) {
return (
<Link
url={url}
label={name}
icon={
<i className="ph ph-bold ph-globe text-primary" />
}
iconOnRight={true}
className={className}
/>
);
} else {
return (
<div className={className}>
{name}
</div>
);
}
};
type SectionProps<T> = {
section: SectionWithItem<T> | CustomSectionGroup;
children?: (item: T) => React.ReactNode;
@ -184,7 +215,7 @@ const Section = <T,>({
<div className="col-span-3 space-y-1">
{children?.(item as T)}
{url !== undefined && <Link url={url} />}
{url !== undefined && section.separateLinks && <Link url={url} />}
{summary !== undefined && !isEmptyString(summary) && (
<div className="wysiwyg" dangerouslySetInnerHTML={{ __html: summary }} />
@ -219,7 +250,7 @@ const Section = <T,>({
<div key={item.id}>
{children?.(item as T)}
{url !== undefined && <Link url={url} />}
{url !== undefined && section.separateLinks && <Link url={url} />}
{summary !== undefined && !isEmptyString(summary) && (
<div className="wysiwyg" dangerouslySetInnerHTML={{ __html: summary }} />
@ -277,7 +308,7 @@ const Experience = () => {
<Section<Experience> section={section} urlKey="url" dateKey="date" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.company}</div>
<LinkedEntity name={item.company} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.position}</div>
<div>{item.location}</div>
</div>
@ -293,7 +324,7 @@ const Education = () => {
<Section<Education> section={section} urlKey="url" dateKey="date" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.institution}</div>
<LinkedEntity name={item.institution} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.area}</div>
<div>{item.studyType}</div>
<div>{item.score}</div>
@ -311,7 +342,7 @@ const Awards = () => {
{(item) => (
<div>
<div className="font-bold">{item.title}</div>
<div>{item.awarder}</div>
<LinkedEntity name={item.awarder} url={item.url} separateLinks={section.separateLinks}/>
</div>
)}
</Section>
@ -326,7 +357,7 @@ const Certifications = () => {
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<div>{item.issuer}</div>
<LinkedEntity name={item.issuer} url={item.url} separateLinks={section.separateLinks}/>
</div>
)}
</Section>
@ -365,7 +396,7 @@ const Publications = () => {
<Section<Publication> section={section} urlKey="url" dateKey="date" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.publisher}</div>
</div>
)}
@ -380,7 +411,7 @@ const Volunteer = () => {
<Section<Volunteer> section={section} urlKey="url" dateKey="date" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.organization}</div>
<LinkedEntity name={item.organization} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.position}</div>
<div>{item.location}</div>
</div>
@ -417,7 +448,7 @@ const Projects = () => {
>
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
</div>
)}
@ -432,7 +463,7 @@ const References = () => {
<Section<Reference> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
</div>
)}
@ -453,7 +484,7 @@ const Custom = ({ id }: { id: string }) => {
>
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
<div>{item.location}</div>
</div>

View File

@ -137,16 +137,17 @@ const Rating = ({ level }: RatingProps) => (
type LinkProps = {
url: URL;
icon?: React.ReactNode;
iconOnRight?: boolean;
label?: string;
className?: string;
};
const Link = ({ url, icon, label, className }: LinkProps) => {
const Link = ({ url, icon, iconOnRight, label, className }: LinkProps) => {
if (!isUrl(url.href)) return null;
return (
<div className="flex items-center gap-x-1.5">
{icon ?? <i className="ph ph-bold ph-link text-primary" />}
{!iconOnRight && ( icon ?? <i className="ph ph-bold ph-link text-primary" />)}
<a
href={url.href}
target="_blank"
@ -155,10 +156,40 @@ const Link = ({ url, icon, label, className }: LinkProps) => {
>
{label || url.label || url.href}
</a>
{iconOnRight && ( icon ?? <i className="ph ph-bold ph-link text-primary" />)}
</div>
);
};
type LinkedEntityProps = {
name: string;
url: URL;
separateLinks: boolean;
className?: string;
};
const LinkedEntity = ({ name, url, separateLinks, className}: LinkedEntityProps) => {
if (!separateLinks && isUrl(url.href)) {
return (
<Link
url={url}
label={name}
icon={
<i className="ph ph-bold ph-globe text-primary" />
}
iconOnRight={true}
className={className}
/>
);
} else {
return (
<div className={className}>
{name}
</div>
);
}
};
type SectionProps<T> = {
section: SectionWithItem<T> | CustomSectionGroup;
children?: (item: T) => React.ReactNode;
@ -200,7 +231,7 @@ const Section = <T,>({
<div key={item.id} className={cn("space-y-2", className)}>
<div>
{children?.(item as T)}
{url !== undefined && <Link url={url} />}
{url !== undefined && section.separateLinks && <Link url={url} />}
</div>
{summary !== undefined && !isEmptyString(summary) && (
@ -228,7 +259,7 @@ const Experience = () => {
{(item) => (
<div className="flex items-start justify-between">
<div className="text-left">
<div className="font-bold">{item.company}</div>
<LinkedEntity name={item.company} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.position}</div>
</div>
@ -250,7 +281,7 @@ const Education = () => {
{(item) => (
<div className="flex items-start justify-between">
<div className="text-left">
<div className="font-bold">{item.institution}</div>
<LinkedEntity name={item.institution} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.area}</div>
<div>{item.score}</div>
</div>
@ -274,7 +305,7 @@ const Awards = () => {
<div className="flex items-start justify-between">
<div className="text-left">
<div className="font-bold">{item.title}</div>
<div>{item.awarder}</div>
<LinkedEntity name={item.awarder} url={item.url} separateLinks={section.separateLinks}/>
</div>
<div className="shrink-0 text-right">
@ -295,7 +326,7 @@ const Certifications = () => {
<div className="flex items-start justify-between">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<div>{item.issuer}</div>
<LinkedEntity name={item.issuer} url={item.url} separateLinks={section.separateLinks}/>
</div>
<div className="shrink-0 text-right">
@ -340,7 +371,7 @@ const Publications = () => {
{(item) => (
<div className="flex items-start justify-between">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.publisher}</div>
</div>
@ -361,7 +392,7 @@ const Volunteer = () => {
{(item) => (
<div className="flex items-start justify-between">
<div className="text-left">
<div className="font-bold">{item.organization}</div>
<LinkedEntity name={item.organization} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.position}</div>
</div>
@ -398,7 +429,7 @@ const Projects = () => {
{(item) => (
<div className="flex items-start justify-between">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
</div>
@ -418,7 +449,7 @@ const References = () => {
<Section<Reference> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
</div>
)}
@ -439,7 +470,7 @@ const Custom = ({ id }: { id: string }) => {
{(item) => (
<div className="flex items-start justify-between">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
</div>

View File

@ -137,16 +137,17 @@ const Rating = ({ level }: RatingProps) => (
type LinkProps = {
url: URL;
icon?: React.ReactNode;
iconOnRight?: boolean;
label?: string;
className?: string;
};
const Link = ({ url, icon, label, className }: LinkProps) => {
const Link = ({ url, icon, iconOnRight, label, className }: LinkProps) => {
if (!isUrl(url.href)) return null;
return (
<div className="flex items-center gap-x-1.5">
{icon ?? <i className="ph ph-bold ph-link text-primary group-[.summary]:text-background" />}
{!iconOnRight && ( icon ?? <i className="ph ph-bold ph-link text-primary group-[.summary]:text-background" />)}
<a
href={url.href}
target="_blank"
@ -155,10 +156,40 @@ const Link = ({ url, icon, label, className }: LinkProps) => {
>
{label || url.label || url.href}
</a>
{iconOnRight && ( icon ?? <i className="ph ph-bold ph-link text-primary group-[.summary]:text-background" />)}
</div>
);
};
type LinkedEntityProps = {
name: string;
url: URL;
separateLinks: boolean;
className?: string;
};
const LinkedEntity = ({ name, url, separateLinks, className}: LinkedEntityProps) => {
if (!separateLinks && isUrl(url.href)) {
return (
<Link
url={url}
label={name}
icon={
<i className="ph ph-bold ph-globe text-primary group-[.summary]:text-background" />
}
iconOnRight={true}
className={className}
/>
);
} else {
return (
<div className={className}>
{name}
</div>
);
}
};
type SectionProps<T> = {
section: SectionWithItem<T> | CustomSectionGroup;
children?: (item: T) => React.ReactNode;
@ -200,7 +231,7 @@ const Section = <T,>({
<div key={item.id} className={cn("space-y-2", className)}>
<div>
{children?.(item as T)}
{url !== undefined && <Link url={url} />}
{url !== undefined && section.separateLinks && <Link url={url} />}
</div>
{summary !== undefined && !isEmptyString(summary) && (
@ -260,7 +291,7 @@ const Experience = () => {
{(item) => (
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.company}</div>
<LinkedEntity name={item.company} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.position}</div>
</div>
@ -282,7 +313,7 @@ const Education = () => {
{(item) => (
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.institution}</div>
<LinkedEntity name={item.institution} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.area}</div>
<div>{item.score}</div>
</div>
@ -306,7 +337,7 @@ const Awards = () => {
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.title}</div>
<div>{item.awarder}</div>
<LinkedEntity name={item.awarder} url={item.url} separateLinks={section.separateLinks}/>
</div>
<div className="shrink-0 text-right group-[.sidebar]:text-left">
@ -327,7 +358,7 @@ const Certifications = () => {
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<div>{item.issuer}</div>
<LinkedEntity name={item.issuer} url={item.url} separateLinks={section.separateLinks}/>
</div>
<div className="shrink-0 text-right group-[.sidebar]:text-left">
@ -372,7 +403,7 @@ const Publications = () => {
{(item) => (
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.publisher}</div>
</div>
@ -393,7 +424,7 @@ const Volunteer = () => {
{(item) => (
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.organization}</div>
<LinkedEntity name={item.organization} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.position}</div>
</div>
@ -430,7 +461,7 @@ const Projects = () => {
{(item) => (
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
</div>
@ -450,7 +481,7 @@ const References = () => {
<Section<Reference> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
</div>
)}
@ -471,7 +502,7 @@ const Custom = ({ id }: { id: string }) => {
{(item) => (
<div className="flex items-start justify-between group-[.sidebar]:flex-col group-[.sidebar]:items-start">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
</div>

View File

@ -109,16 +109,17 @@ const Rating = ({ level }: RatingProps) => (
type LinkProps = {
url: URL;
icon?: React.ReactNode;
iconOnRight?: boolean;
label?: string;
className?: string;
};
const Link = ({ url, icon, label, className }: LinkProps) => {
const Link = ({ url, icon, iconOnRight, label, className }: LinkProps) => {
if (!isUrl(url.href)) return null;
return (
<div className="flex items-center gap-x-1.5">
{icon ?? <i className="ph ph-bold ph-link text-primary" />}
{!iconOnRight && ( icon ?? <i className="ph ph-bold ph-link text-primary" />)}
<a
href={url.href}
target="_blank"
@ -127,10 +128,40 @@ const Link = ({ url, icon, label, className }: LinkProps) => {
>
{label || url.label || url.href}
</a>
{iconOnRight && ( icon ?? <i className="ph ph-bold ph-link text-primary" />)}
</div>
);
};
type LinkedEntityProps = {
name: string;
url: URL;
separateLinks: boolean;
className?: string;
};
const LinkedEntity = ({ name, url, separateLinks, className}: LinkedEntityProps) => {
if (!separateLinks && isUrl(url.href)) {
return (
<Link
url={url}
label={name}
icon={
<i className="ph ph-bold ph-globe text-primary" />
}
iconOnRight={true}
className={className}
/>
);
} else {
return (
<div className={className}>
{name}
</div>
);
}
};
type SectionProps<T> = {
section: SectionWithItem<T> | CustomSectionGroup;
children?: (item: T) => React.ReactNode;
@ -172,7 +203,7 @@ const Section = <T,>({
<div key={item.id} className={cn("space-y-2", className)}>
<div>
{children?.(item as T)}
{url !== undefined && <Link url={url} />}
{url !== undefined && section.separateLinks && <Link url={url} />}
</div>
{summary !== undefined && !isEmptyString(summary) && (
@ -232,7 +263,7 @@ const Experience = () => {
{(item) => (
<div className="flex items-start justify-between">
<div className="text-left">
<div className="font-bold">{item.company}</div>
<LinkedEntity name={item.company} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.position}</div>
</div>
@ -254,7 +285,7 @@ const Education = () => {
{(item) => (
<div className="flex items-start justify-between">
<div className="text-left">
<div className="font-bold">{item.institution}</div>
<LinkedEntity name={item.institution} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.area}</div>
<div>{item.score}</div>
</div>
@ -278,7 +309,7 @@ const Awards = () => {
<div className="flex items-start justify-between">
<div className="text-left">
<div className="font-bold">{item.title}</div>
<div>{item.awarder}</div>
<LinkedEntity name={item.awarder} url={item.url} separateLinks={section.separateLinks}/>
</div>
<div className="shrink-0 text-right">
@ -299,7 +330,7 @@ const Certifications = () => {
<div className="flex items-start justify-between">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<div>{item.issuer}</div>
<LinkedEntity name={item.issuer} url={item.url} separateLinks={section.separateLinks}/>
</div>
<div className="shrink-0 text-right">
@ -344,7 +375,7 @@ const Publications = () => {
{(item) => (
<div className="flex items-start justify-between">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.publisher}</div>
</div>
@ -365,7 +396,7 @@ const Volunteer = () => {
{(item) => (
<div className="flex items-start justify-between">
<div className="text-left">
<div className="font-bold">{item.organization}</div>
<LinkedEntity name={item.organization} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.position}</div>
</div>
@ -402,7 +433,7 @@ const Projects = () => {
{(item) => (
<div className="flex items-start justify-between">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
</div>
@ -422,7 +453,7 @@ const References = () => {
<Section<Reference> section={section} urlKey="url" summaryKey="summary">
{(item) => (
<div>
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
</div>
)}
@ -443,7 +474,7 @@ const Custom = ({ id }: { id: string }) => {
{(item) => (
<div className="flex items-start justify-between">
<div className="text-left">
<div className="font-bold">{item.name}</div>
<LinkedEntity name={item.name} url={item.url} separateLinks={section.separateLinks} className="font-bold"/>
<div>{item.description}</div>
</div>

View File

@ -10,10 +10,11 @@ import {
Plus,
TrashSimple,
} from "@phosphor-icons/react";
import { defaultSections, SectionKey, SectionWithItem } from "@reactive-resume/schema";
import { defaultSections, SectionKey, sectionsSchema, SectionWithItem } from "@reactive-resume/schema";
import {
Button,
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
@ -31,6 +32,9 @@ import { useMemo } from "react";
import { useDialog } from "@/client/stores/dialog";
import { useResumeStore } from "@/client/stores/resume";
import { DropdownMenuItemIndicator } from "@radix-ui/react-dropdown-menu";
import { CheckboxIndicator } from "@radix-ui/react-checkbox";
import { itemsEqual } from "@dnd-kit/sortable/dist/utilities";
type Props = { id: SectionKey };
@ -47,6 +51,7 @@ export const SectionOptions = ({ id }: Props) => {
const isCustomSection = useMemo(() => id.startsWith("custom"), [id]);
const onCreate = () => open("create", { id });
const toggleSeperateLinks = (checked: boolean) => setValue(`sections.${id}.separateLinks`, checked);
const toggleVisibility = () => setValue(`sections.${id}.visible`, !section.visible);
const onResetName = () => setValue(`sections.${id}.name`, originalName);
const onChangeColumns = (value: string) => setValue(`sections.${id}.columns`, Number(value));
@ -67,7 +72,12 @@ export const SectionOptions = ({ id }: Props) => {
<Plus />
<span className="ml-2">{t`Add a new item`}</span>
</DropdownMenuItem>
<DropdownMenuCheckboxItem
onCheckedChange={toggleSeperateLinks}
checked={section.separateLinks}
>
<span className="ml-0">{t`Separate Links`}</span>
</DropdownMenuCheckboxItem>
<DropdownMenuSeparator />
</>
)}

View File

@ -28,6 +28,7 @@ export const sampleResume: ResumeData = {
summary: {
name: "Summary",
columns: 1,
separateLinks: true,
visible: true,
id: "summary",
content:
@ -36,6 +37,7 @@ export const sampleResume: ResumeData = {
awards: {
name: "Awards",
columns: 1,
separateLinks: true,
visible: true,
id: "awards",
items: [],
@ -43,6 +45,7 @@ export const sampleResume: ResumeData = {
certifications: {
name: "Certifications",
columns: 1,
separateLinks: true,
visible: true,
id: "certifications",
items: [
@ -75,6 +78,7 @@ export const sampleResume: ResumeData = {
education: {
name: "Education",
columns: 1,
separateLinks: true,
visible: true,
id: "education",
items: [
@ -97,6 +101,7 @@ export const sampleResume: ResumeData = {
experience: {
name: "Experience",
columns: 1,
separateLinks: true,
visible: true,
id: "experience",
items: [
@ -133,6 +138,7 @@ export const sampleResume: ResumeData = {
volunteer: {
name: "Volunteering",
columns: 1,
separateLinks: true,
visible: true,
id: "volunteer",
items: [],
@ -140,6 +146,7 @@ export const sampleResume: ResumeData = {
interests: {
name: "Interests",
columns: 1,
separateLinks: true,
visible: true,
id: "interests",
items: [],
@ -147,6 +154,7 @@ export const sampleResume: ResumeData = {
languages: {
name: "Languages",
columns: 1,
separateLinks: true,
visible: true,
id: "languages",
items: [],
@ -154,6 +162,7 @@ export const sampleResume: ResumeData = {
profiles: {
name: "Profiles",
columns: 1,
separateLinks: true,
visible: true,
id: "profiles",
items: [
@ -184,6 +193,7 @@ export const sampleResume: ResumeData = {
projects: {
name: "Projects",
columns: 1,
separateLinks: true,
visible: true,
id: "projects",
items: [
@ -220,6 +230,7 @@ export const sampleResume: ResumeData = {
publications: {
name: "Publications",
columns: 1,
separateLinks: true,
visible: true,
id: "publications",
items: [],
@ -227,6 +238,7 @@ export const sampleResume: ResumeData = {
references: {
name: "References",
columns: 1,
separateLinks: true,
visible: false,
id: "references",
items: [
@ -246,6 +258,7 @@ export const sampleResume: ResumeData = {
skills: {
name: "Skills",
columns: 1,
separateLinks: true,
visible: true,
id: "skills",
items: [

View File

@ -20,6 +20,7 @@ import { volunteerSchema } from "./volunteer";
export const sectionSchema = z.object({
name: z.string(),
columns: z.number().min(1).max(5).default(1),
separateLinks: z.boolean().default(true),
visible: z.boolean().default(true),
});
@ -98,6 +99,7 @@ export type CustomSectionGroup = z.infer<typeof customSchema>;
export const defaultSection: Section = {
name: "",
columns: 1,
separateLinks: true,
visible: true,
};