mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-07-26 01:44:53 +10:00
chore: add missing translations
This commit is contained in:
@@ -191,6 +191,24 @@ describe("registerFonts", () => {
|
||||
expect(pdfTypography.body.fontWeights).toEqual(["400", "600", "800"]);
|
||||
expect(pdfTypography.heading.fontWeights).toEqual(["500", "900"]);
|
||||
});
|
||||
|
||||
it("replaces unsupported font weights with an available fallback pair", async () => {
|
||||
const registerSpy = vi.spyOn(Font, "register").mockImplementation(() => {});
|
||||
const { registerFonts } = await import("./use-register-fonts");
|
||||
|
||||
const migratedTypography = {
|
||||
...typography,
|
||||
body: { ...typography.body, fontFamily: "Lato", fontWeights: ["400"] },
|
||||
heading: { ...typography.heading, fontFamily: "Lato", fontWeights: ["600"] },
|
||||
} satisfies Typography;
|
||||
|
||||
const pdfTypography = registerFonts(migratedTypography, "en-US");
|
||||
|
||||
expect(pdfTypography.body.fontWeights).toEqual(["400"]);
|
||||
expect(pdfTypography.heading.fontWeights).toEqual(["400", "700"]);
|
||||
expect(registerSpy).not.toHaveBeenCalledWith(expect.objectContaining({ family: "Lato", fontWeight: 600 }));
|
||||
expect(registerSpy).toHaveBeenCalledWith(expect.objectContaining({ family: "Lato", fontWeight: 700 }));
|
||||
});
|
||||
});
|
||||
|
||||
describe("resumeContentContainsCJK", () => {
|
||||
|
||||
@@ -21,6 +21,8 @@ type FontWeightRange = {
|
||||
const registeredFontVariants = new Set<string>();
|
||||
const fallbackFontFamily = "IBM Plex Serif";
|
||||
const cjkLetterRegex = cjkLetters().toRegExp();
|
||||
const fontWeightValues = new Set<FontWeight>(["100", "200", "300", "400", "500", "600", "700", "800", "900"]);
|
||||
const preferredFallbackFontWeights = ["400", "700", "600", "500"] satisfies FontWeight[];
|
||||
|
||||
// `fontFamily` is widened to `string | string[]` so react-pdf can do
|
||||
// glyph-level font fallback for CJK characters (#2986).
|
||||
@@ -46,6 +48,52 @@ const getFontWeightRange = (fontWeights: string[]): FontWeightRange => {
|
||||
return { lowest, highest };
|
||||
};
|
||||
|
||||
const isFontWeight = (weight: string): weight is FontWeight => fontWeightValues.has(weight as FontWeight);
|
||||
|
||||
const uniqueSortedFontWeights = (fontWeights: FontWeight[]): FontWeight[] => {
|
||||
return [...new Set(sortFontWeights(fontWeights))];
|
||||
};
|
||||
|
||||
const getFallbackFontWeights = (availableWeights: FontWeight[]): FontWeight[] => {
|
||||
const sortedAvailableWeights = uniqueSortedFontWeights(availableWeights);
|
||||
const availableWeightSet = new Set(sortedAvailableWeights);
|
||||
const preferredWeights = preferredFallbackFontWeights.filter((weight) => availableWeightSet.has(weight));
|
||||
|
||||
if (preferredWeights.length >= 2) {
|
||||
return uniqueSortedFontWeights(preferredWeights.slice(0, 2));
|
||||
}
|
||||
|
||||
if (preferredWeights.length === 1) {
|
||||
const firstWeight = preferredWeights[0];
|
||||
if (!firstWeight) return [];
|
||||
|
||||
const secondWeight =
|
||||
sortedAvailableWeights.find((weight) => Number(weight) > Number(firstWeight)) ??
|
||||
sortedAvailableWeights.find((weight) => weight !== firstWeight);
|
||||
|
||||
return uniqueSortedFontWeights(secondWeight ? [firstWeight, secondWeight] : [firstWeight]);
|
||||
}
|
||||
|
||||
return sortedAvailableWeights.slice(0, 2);
|
||||
};
|
||||
|
||||
const resolvePdfFontWeights = (family: string, fontWeights: string[]): FontWeight[] => {
|
||||
const requestedWeights = uniqueSortedFontWeights(fontWeights.filter(isFontWeight));
|
||||
const availableWeights = getFont(family)?.weights;
|
||||
|
||||
if (!availableWeights || availableWeights.length === 0) {
|
||||
return requestedWeights.length > 0 ? requestedWeights : ["400", "700"];
|
||||
}
|
||||
|
||||
const availableWeightSet = new Set(availableWeights);
|
||||
const allRequestedWeightsAreAvailable =
|
||||
requestedWeights.length > 0 && requestedWeights.every((weight) => availableWeightSet.has(weight));
|
||||
|
||||
if (allRequestedWeightsAreAvailable) return requestedWeights;
|
||||
|
||||
return getFallbackFontWeights(availableWeights);
|
||||
};
|
||||
|
||||
const toFontWeight = (weight: number): FontWeight => {
|
||||
if (weight <= 100) return "100";
|
||||
if (weight <= 200) return "200";
|
||||
@@ -82,8 +130,8 @@ const resolvePdfFontFamily = (family: string) => {
|
||||
const resolvePdfTypography = (typography: Typography): Typography => {
|
||||
const bodyFontFamily = resolvePdfFontFamily(typography.body.fontFamily);
|
||||
const headingFontFamily = resolvePdfFontFamily(typography.heading.fontFamily);
|
||||
const bodyFontWeights = sortFontWeights(typography.body.fontWeights);
|
||||
const headingFontWeights = sortFontWeights(typography.heading.fontWeights);
|
||||
const bodyFontWeights = resolvePdfFontWeights(bodyFontFamily, typography.body.fontWeights);
|
||||
const headingFontWeights = resolvePdfFontWeights(headingFontFamily, typography.heading.fontWeights);
|
||||
|
||||
return {
|
||||
...typography,
|
||||
|
||||
@@ -42,6 +42,10 @@ type AzurillTemplate = {
|
||||
featureStyles: TemplateFeatureStyleSlots;
|
||||
};
|
||||
|
||||
type AzurillHeaderProps = {
|
||||
styles: AzurillStyles;
|
||||
};
|
||||
|
||||
const azurillFeatures = {
|
||||
sectionTimeline: true,
|
||||
} satisfies TemplateFeatures;
|
||||
@@ -88,7 +92,7 @@ export const AzurillPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const Header = ({ styles }: { styles: AzurillStyles }) => {
|
||||
const Header = ({ styles }: AzurillHeaderProps) => {
|
||||
const { basics, picture } = useRender();
|
||||
const hasPicture = hasTemplatePicture(picture);
|
||||
|
||||
|
||||
@@ -33,6 +33,10 @@ type BronzorTemplate = {
|
||||
styles: BronzorStyles;
|
||||
};
|
||||
|
||||
type BronzorHeaderProps = {
|
||||
styles: BronzorStyles;
|
||||
};
|
||||
|
||||
const getBronzorSections = ({
|
||||
mainSections,
|
||||
sidebarSections,
|
||||
@@ -85,7 +89,7 @@ export const BronzorPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const Header = ({ styles }: { styles: BronzorStyles }) => {
|
||||
const Header = ({ styles }: BronzorHeaderProps) => {
|
||||
const { basics, picture } = useRender();
|
||||
const hasPicture = hasTemplatePicture(picture);
|
||||
|
||||
|
||||
@@ -35,6 +35,10 @@ type ChikoritaTemplate = {
|
||||
styles: ChikoritaStyles;
|
||||
};
|
||||
|
||||
type ChikoritaHeaderProps = {
|
||||
styles: ChikoritaStyles;
|
||||
};
|
||||
|
||||
export const ChikoritaPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
const data = useRender();
|
||||
const { metadata, picture } = data;
|
||||
@@ -91,7 +95,7 @@ export const ChikoritaPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const Header = ({ styles }: { styles: ChikoritaStyles }) => {
|
||||
const Header = ({ styles }: ChikoritaHeaderProps) => {
|
||||
const { basics, picture } = useRender();
|
||||
const hasPicture = hasTemplatePicture(picture);
|
||||
|
||||
|
||||
@@ -39,6 +39,11 @@ type DitgarTemplate = {
|
||||
styles: DitgarStyles;
|
||||
};
|
||||
|
||||
type DitgarHeaderProps = {
|
||||
styles: DitgarStyles;
|
||||
colors: TemplateColorRoles;
|
||||
};
|
||||
|
||||
const ditgarFeatures = {
|
||||
stackSidebarItemHeader: true,
|
||||
mainItemHeaderBorder: true,
|
||||
@@ -102,7 +107,7 @@ export const DitgarPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const Header = ({ styles, colors }: { styles: DitgarStyles; colors: TemplateColorRoles }) => {
|
||||
const Header = ({ styles, colors }: DitgarHeaderProps) => {
|
||||
const { basics, picture } = useRender();
|
||||
const hasPicture = hasTemplatePicture(picture);
|
||||
|
||||
|
||||
@@ -40,6 +40,10 @@ type DittoTemplate = {
|
||||
styles: DittoStyles;
|
||||
};
|
||||
|
||||
type DittoHeaderProps = {
|
||||
styles: DittoStyles;
|
||||
};
|
||||
|
||||
export const DittoPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
const data = useRender();
|
||||
const { metadata, picture } = data;
|
||||
@@ -91,7 +95,7 @@ export const DittoPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const Header = ({ styles }: { styles: DittoStyles }) => {
|
||||
const Header = ({ styles }: DittoHeaderProps) => {
|
||||
const { basics, picture } = useRender();
|
||||
const hasPicture = hasTemplatePicture(picture);
|
||||
|
||||
|
||||
@@ -39,6 +39,11 @@ type GengarTemplate = {
|
||||
styles: GengarStyles;
|
||||
};
|
||||
|
||||
type GengarHeaderProps = {
|
||||
styles: GengarStyles;
|
||||
colors: TemplateColorRoles;
|
||||
};
|
||||
|
||||
export const GengarPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
const data = useRender();
|
||||
const { metadata } = data;
|
||||
@@ -99,7 +104,7 @@ export const GengarPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const Header = ({ styles, colors }: { styles: GengarStyles; colors: TemplateColorRoles }) => {
|
||||
const Header = ({ styles, colors }: GengarHeaderProps) => {
|
||||
const { basics, picture } = useRender();
|
||||
const hasPicture = hasTemplatePicture(picture);
|
||||
|
||||
|
||||
@@ -38,6 +38,10 @@ type GlalieTemplate = {
|
||||
styles: GlalieStyles;
|
||||
};
|
||||
|
||||
type GlalieHeaderProps = {
|
||||
styles: GlalieStyles;
|
||||
};
|
||||
|
||||
const glalieFeatures = {
|
||||
stackSidebarItemHeader: true,
|
||||
} satisfies TemplateFeatures;
|
||||
@@ -91,7 +95,7 @@ export const GlaliePage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const Header = ({ styles }: { styles: GlalieStyles }) => {
|
||||
const Header = ({ styles }: GlalieHeaderProps) => {
|
||||
const { basics, picture } = useRender();
|
||||
const hasPicture = hasTemplatePicture(picture);
|
||||
|
||||
|
||||
@@ -34,6 +34,10 @@ type KakunaTemplate = {
|
||||
styles: KakunaStyles;
|
||||
};
|
||||
|
||||
type KakunaHeaderProps = {
|
||||
styles: KakunaStyles;
|
||||
};
|
||||
|
||||
export const KakunaPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
const data = useRender();
|
||||
const { metadata } = data;
|
||||
@@ -68,7 +72,7 @@ export const KakunaPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const Header = ({ styles }: { styles: KakunaStyles }) => {
|
||||
const Header = ({ styles }: KakunaHeaderProps) => {
|
||||
const { basics, picture } = useRender();
|
||||
const hasPicture = hasTemplatePicture(picture);
|
||||
|
||||
|
||||
@@ -33,6 +33,10 @@ type LaprasTemplate = {
|
||||
styles: LaprasStyles;
|
||||
};
|
||||
|
||||
type LaprasHeaderProps = {
|
||||
styles: LaprasStyles;
|
||||
};
|
||||
|
||||
export const LaprasPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
const data = useRender();
|
||||
const { metadata } = data;
|
||||
@@ -67,7 +71,7 @@ export const LaprasPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const Header = ({ styles }: { styles: LaprasStyles }) => {
|
||||
const Header = ({ styles }: LaprasHeaderProps) => {
|
||||
const { basics, picture } = useRender();
|
||||
const hasPicture = hasTemplatePicture(picture);
|
||||
|
||||
|
||||
@@ -38,6 +38,10 @@ type LeafishTemplate = {
|
||||
styles: LeafishStyles;
|
||||
};
|
||||
|
||||
type LeafishHeaderProps = {
|
||||
styles: LeafishStyles;
|
||||
};
|
||||
|
||||
export const LeafishPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
const data = useRender();
|
||||
const { metadata } = data;
|
||||
@@ -79,7 +83,7 @@ export const LeafishPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const Header = ({ styles }: { styles: LeafishStyles }) => {
|
||||
const Header = ({ styles }: LeafishHeaderProps) => {
|
||||
const { basics, picture } = useRender();
|
||||
const hasPicture = hasTemplatePicture(picture);
|
||||
|
||||
|
||||
@@ -34,6 +34,10 @@ type MeowthTemplate = {
|
||||
styles: MeowthStyles;
|
||||
};
|
||||
|
||||
type MeowthHeaderProps = {
|
||||
styles: MeowthStyles;
|
||||
};
|
||||
|
||||
const meowthFeatures = {
|
||||
inlineItemHeader: true,
|
||||
} satisfies TemplateFeatures;
|
||||
@@ -72,7 +76,7 @@ export const MeowthPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const Header = ({ styles }: { styles: MeowthStyles }) => {
|
||||
const Header = ({ styles }: MeowthHeaderProps) => {
|
||||
const { basics, picture } = useRender();
|
||||
const hasPicture = hasTemplatePicture(picture);
|
||||
|
||||
|
||||
@@ -33,6 +33,10 @@ type OnyxTemplate = {
|
||||
styles: OnyxStyles;
|
||||
};
|
||||
|
||||
type OnyxHeaderProps = {
|
||||
styles: OnyxStyles;
|
||||
};
|
||||
|
||||
export const OnyxPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
const data = useRender();
|
||||
const { metadata } = data;
|
||||
@@ -67,7 +71,7 @@ export const OnyxPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const Header = ({ styles }: { styles: OnyxStyles }) => {
|
||||
const Header = ({ styles }: OnyxHeaderProps) => {
|
||||
const { basics, picture } = useRender();
|
||||
const hasPicture = hasTemplatePicture(picture);
|
||||
|
||||
|
||||
@@ -38,6 +38,11 @@ type PikachuTemplate = {
|
||||
styles: PikachuStyles;
|
||||
};
|
||||
|
||||
type PikachuHeaderProps = {
|
||||
styles: PikachuStyles;
|
||||
colors: TemplateColorRoles;
|
||||
};
|
||||
|
||||
const pikachuFeatures = {
|
||||
stackSidebarItemHeader: true,
|
||||
} satisfies TemplateFeatures;
|
||||
@@ -96,7 +101,7 @@ export const PikachuPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const Header = ({ styles, colors }: { styles: PikachuStyles; colors: TemplateColorRoles }) => {
|
||||
const Header = ({ styles, colors }: PikachuHeaderProps) => {
|
||||
const { basics } = useRender();
|
||||
|
||||
return (
|
||||
|
||||
@@ -36,6 +36,10 @@ type RhyhornTemplate = {
|
||||
styles: RhyhornStyles;
|
||||
};
|
||||
|
||||
type RhyhornHeaderProps = {
|
||||
styles: RhyhornStyles;
|
||||
};
|
||||
|
||||
export const RhyhornPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
const data = useRender();
|
||||
const { metadata } = data;
|
||||
@@ -70,7 +74,7 @@ export const RhyhornPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const Header = ({ styles }: { styles: RhyhornStyles }) => {
|
||||
const Header = ({ styles }: RhyhornHeaderProps) => {
|
||||
const { basics, picture } = useRender();
|
||||
const hasPicture = hasTemplatePicture(picture);
|
||||
const contactItems: {
|
||||
|
||||
@@ -34,6 +34,10 @@ type ScizorTemplate = {
|
||||
styles: ScizorStyles;
|
||||
};
|
||||
|
||||
type ScizorHeaderProps = {
|
||||
styles: ScizorStyles;
|
||||
};
|
||||
|
||||
export const ScizorPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
const data = useRender();
|
||||
const { metadata } = data;
|
||||
@@ -61,7 +65,7 @@ export const ScizorPage = ({ page, pageIndex }: TemplatePageProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
const Header = ({ styles }: { styles: ScizorStyles }) => {
|
||||
const Header = ({ styles }: ScizorHeaderProps) => {
|
||||
const { basics, picture } = useRender();
|
||||
const hasPicture = hasTemplatePicture(picture);
|
||||
|
||||
|
||||
@@ -12,17 +12,21 @@ type WebsiteDisplay = {
|
||||
|
||||
type ContactStyle = Style | Style[];
|
||||
|
||||
export const WebsiteContactItem = ({
|
||||
website,
|
||||
style,
|
||||
textStyle,
|
||||
iconColor,
|
||||
}: {
|
||||
type WebsiteContactItemProps = {
|
||||
website: WebsiteDisplay;
|
||||
style?: ContactStyle;
|
||||
textStyle?: ContactStyle;
|
||||
iconColor?: string;
|
||||
}) => {
|
||||
};
|
||||
|
||||
type CustomFieldContactItemProps = {
|
||||
field: CustomField;
|
||||
style?: ContactStyle;
|
||||
textStyle?: ContactStyle;
|
||||
iconColor?: string;
|
||||
};
|
||||
|
||||
export const WebsiteContactItem = ({ website, style, textStyle, iconColor }: WebsiteContactItemProps) => {
|
||||
if (!website.url) return null;
|
||||
|
||||
return (
|
||||
@@ -33,17 +37,7 @@ export const WebsiteContactItem = ({
|
||||
);
|
||||
};
|
||||
|
||||
export const CustomFieldContactItem = ({
|
||||
field,
|
||||
style,
|
||||
textStyle,
|
||||
iconColor,
|
||||
}: {
|
||||
field: CustomField;
|
||||
style?: ContactStyle;
|
||||
textStyle?: ContactStyle;
|
||||
iconColor?: string;
|
||||
}) => {
|
||||
export const CustomFieldContactItem = ({ field, style, textStyle, iconColor }: CustomFieldContactItemProps) => {
|
||||
const linkUrl = getCustomFieldLinkUrl(field);
|
||||
const children = (
|
||||
<>
|
||||
|
||||
@@ -29,6 +29,16 @@ type TemplateProviderProps = Omit<TemplateContextValue, "featureStyles" | "featu
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
type TemplatePlacementProviderProps = {
|
||||
placement: TemplatePlacement;
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
type SectionStyleProviderProps = {
|
||||
context: SectionStyleRuleContext;
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
const TemplateContext = createContext<TemplateContextValue | null>(null);
|
||||
const TemplatePlacementContext = createContext<TemplatePlacement>("main");
|
||||
const SectionStyleContext = createContext<SectionStyleRuleContext | null>(null);
|
||||
@@ -73,23 +83,11 @@ export const TemplateProvider = ({
|
||||
return <TemplateContext.Provider value={contextValue}>{children}</TemplateContext.Provider>;
|
||||
};
|
||||
|
||||
export const TemplatePlacementProvider = ({
|
||||
placement,
|
||||
children,
|
||||
}: {
|
||||
placement: TemplatePlacement;
|
||||
children: ReactNode;
|
||||
}) => {
|
||||
export const TemplatePlacementProvider = ({ placement, children }: TemplatePlacementProviderProps) => {
|
||||
return <TemplatePlacementContext.Provider value={placement}>{children}</TemplatePlacementContext.Provider>;
|
||||
};
|
||||
|
||||
export const SectionStyleProvider = ({
|
||||
context,
|
||||
children,
|
||||
}: {
|
||||
context: SectionStyleRuleContext;
|
||||
children: ReactNode;
|
||||
}) => {
|
||||
export const SectionStyleProvider = ({ context, children }: SectionStyleProviderProps) => {
|
||||
return <SectionStyleContext.Provider value={context}>{children}</SectionStyleContext.Provider>;
|
||||
};
|
||||
|
||||
|
||||
@@ -9,7 +9,11 @@ import { composeStyles } from "./styles";
|
||||
|
||||
const LEVEL_ITEM_KEYS = ["level-1", "level-2", "level-3", "level-4", "level-5"] as const;
|
||||
|
||||
export const LevelDisplay = ({ level }: { level: number }) => {
|
||||
type LevelDisplayProps = {
|
||||
level: number;
|
||||
};
|
||||
|
||||
export const LevelDisplay = ({ level }: LevelDisplayProps) => {
|
||||
const data = useRender();
|
||||
const levelDesign = data.metadata.design.level;
|
||||
const iconSize = data.metadata.typography.body.fontSize - 2;
|
||||
|
||||
@@ -22,6 +22,10 @@ const richListItemContentStackStyle = {
|
||||
flexDirection: "column",
|
||||
} satisfies Style;
|
||||
|
||||
type RichTextProps = {
|
||||
children: string;
|
||||
};
|
||||
|
||||
// react-pdf textkit reads BiDi base direction from each run's own `direction` attribute
|
||||
// (default "ltr"), and react-pdf-html buckets inline content into styleless inner <Text>
|
||||
// frames — so the rtl style has to be injected onto every descendant, not just a wrapper.
|
||||
@@ -53,7 +57,7 @@ const applyRtlDirectionRecursively = (node: ReactNode): ReactNode => {
|
||||
return cloneElement(element, { style: nextStyle }, nextChildren);
|
||||
};
|
||||
|
||||
export const RichText = ({ children }: { children: string }) => {
|
||||
export const RichText = ({ children }: RichTextProps) => {
|
||||
const { rtl } = useRender();
|
||||
const rtlTextWrapStyle: Style | undefined = rtl ? { direction: "rtl", textAlign: "right" } : undefined;
|
||||
const boldStyle = useTemplateStyle("bold");
|
||||
|
||||
@@ -51,6 +51,77 @@ type SectionItemsContextValue = {
|
||||
useTimeline: boolean;
|
||||
};
|
||||
|
||||
type SectionShellProps = {
|
||||
sectionId: string;
|
||||
title: string;
|
||||
showHeading?: boolean;
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
type SectionItemsProps = {
|
||||
children: ReactNode;
|
||||
columns?: number;
|
||||
};
|
||||
|
||||
type SectionItemProps = {
|
||||
children: ReactNode;
|
||||
style?: StyleInput;
|
||||
};
|
||||
|
||||
type InlineItemHeaderProps = {
|
||||
leading?: ReactNode;
|
||||
middle?: ReactNode;
|
||||
trailing?: ReactNode;
|
||||
};
|
||||
|
||||
type SectionItemHeaderProps = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
type ItemWebsite = {
|
||||
url: string;
|
||||
label: string;
|
||||
inlineLink?: boolean | undefined;
|
||||
};
|
||||
|
||||
type ItemTitleProps = {
|
||||
children: ReactNode;
|
||||
website: ItemWebsite;
|
||||
};
|
||||
|
||||
type ItemWebsiteLinkProps = {
|
||||
website: ItemWebsite;
|
||||
};
|
||||
|
||||
type SummarySectionProps = {
|
||||
showHeading?: boolean;
|
||||
};
|
||||
|
||||
type CustomSummarySectionProps = {
|
||||
section: CustomItemSection<SummaryItem>;
|
||||
showHeading?: boolean;
|
||||
};
|
||||
|
||||
type ItemSectionProps<T> = {
|
||||
sectionId?: string;
|
||||
sectionData?: ItemSection<T>;
|
||||
};
|
||||
|
||||
type CoverLetterSectionProps = {
|
||||
section: CustomItemSection<CoverLetterItem>;
|
||||
};
|
||||
|
||||
type CustomSectionProps = {
|
||||
sectionId: string;
|
||||
showHeading?: boolean;
|
||||
};
|
||||
|
||||
type SectionProps = {
|
||||
section: string;
|
||||
placement: TemplatePlacement;
|
||||
showHeading?: boolean;
|
||||
};
|
||||
|
||||
const SectionItemsContext = createContext<SectionItemsContextValue>({ itemStyle: undefined, useTimeline: false });
|
||||
const SECTION_ITEM_PLACEHOLDER_KEYS = [
|
||||
"placeholder-1",
|
||||
@@ -78,17 +149,7 @@ const getVisibleItems = <T extends { hidden: boolean }>(section: ItemSection<T>,
|
||||
return filterItems(section.items, sectionType);
|
||||
};
|
||||
|
||||
const SectionShell = ({
|
||||
sectionId,
|
||||
title,
|
||||
showHeading = true,
|
||||
children,
|
||||
}: {
|
||||
sectionId: string;
|
||||
title: string;
|
||||
showHeading?: boolean;
|
||||
children: ReactNode;
|
||||
}) => {
|
||||
const SectionShell = ({ sectionId, title, showHeading = true, children }: SectionShellProps) => {
|
||||
const data = useRender();
|
||||
const sectionStyle = useTemplateStyle("section");
|
||||
const sectionRuleStyle = useSectionStyleRule("section");
|
||||
@@ -106,7 +167,7 @@ const SectionShell = ({
|
||||
);
|
||||
};
|
||||
|
||||
const SectionItems = ({ children, columns = 1 }: { children: ReactNode; columns?: number }) => {
|
||||
const SectionItems = ({ children, columns = 1 }: SectionItemsProps) => {
|
||||
const data = useRender();
|
||||
const placement = useTemplatePlacement();
|
||||
const sectionTimeline = useTemplateFeature("sectionTimeline");
|
||||
@@ -164,7 +225,7 @@ const SectionItems = ({ children, columns = 1 }: { children: ReactNode; columns?
|
||||
);
|
||||
};
|
||||
|
||||
const SectionItem = ({ children, style }: { children: ReactNode; style?: StyleInput }) => {
|
||||
const SectionItem = ({ children, style }: SectionItemProps) => {
|
||||
const { itemStyle: sectionItemStyle, useTimeline } = useSectionItemsContext();
|
||||
const itemStyle = useTemplateStyle("item");
|
||||
const itemRuleStyle = useSectionStyleRule("item");
|
||||
@@ -187,15 +248,7 @@ const SectionItem = ({ children, style }: { children: ReactNode; style?: StyleIn
|
||||
);
|
||||
};
|
||||
|
||||
const InlineItemHeader = ({
|
||||
leading,
|
||||
middle,
|
||||
trailing,
|
||||
}: {
|
||||
leading?: ReactNode;
|
||||
middle?: ReactNode;
|
||||
trailing?: ReactNode;
|
||||
}) => {
|
||||
const InlineItemHeader = ({ leading, middle, trailing }: InlineItemHeaderProps) => {
|
||||
const inlineItemHeaderStyle = useTemplateStyle("inlineItemHeader");
|
||||
const leadingStyle = useTemplateStyle("inlineItemHeaderLeading");
|
||||
const middleStyle = useTemplateStyle("inlineItemHeaderMiddle");
|
||||
@@ -232,7 +285,7 @@ const useSectionSplitRowStyle = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const SectionItemHeader = ({ children }: { children: ReactNode }) => {
|
||||
const SectionItemHeader = ({ children }: SectionItemHeaderProps) => {
|
||||
const mainItemHeaderBorder = useTemplateFeature("mainItemHeaderBorder");
|
||||
const sectionItemHeaderStyle = useTemplateStyle("sectionItemHeader");
|
||||
|
||||
@@ -241,13 +294,7 @@ 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 ItemTitle = ({ children, website }: ItemTitleProps) => {
|
||||
const inlineWebsiteUrl = getInlineItemWebsiteUrl(website);
|
||||
const title = <Bold>{children}</Bold>;
|
||||
|
||||
@@ -256,13 +303,13 @@ const ItemTitle = ({ children, website }: { children: ReactNode; website: ItemWe
|
||||
return <Link src={inlineWebsiteUrl}>{title}</Link>;
|
||||
};
|
||||
|
||||
const ItemWebsiteLink = ({ website }: { website: ItemWebsite }) => {
|
||||
const ItemWebsiteLink = ({ website }: ItemWebsiteLinkProps) => {
|
||||
if (!shouldRenderSeparateItemWebsite(website)) return null;
|
||||
|
||||
return <Link src={website.url}>{getWebsiteDisplayText(website)}</Link>;
|
||||
};
|
||||
|
||||
const SummarySection = ({ showHeading = true }: { showHeading?: boolean } = {}) => {
|
||||
const SummarySection = ({ showHeading = true }: SummarySectionProps = {}) => {
|
||||
const data = useRender();
|
||||
const { summary } = data;
|
||||
|
||||
@@ -279,13 +326,7 @@ const SummarySection = ({ showHeading = true }: { showHeading?: boolean } = {})
|
||||
);
|
||||
};
|
||||
|
||||
const CustomSummarySection = ({
|
||||
section,
|
||||
showHeading = true,
|
||||
}: {
|
||||
section: CustomItemSection<SummaryItem>;
|
||||
showHeading?: boolean;
|
||||
}) => {
|
||||
const CustomSummarySection = ({ section, showHeading = true }: CustomSummarySectionProps) => {
|
||||
const items = getVisibleItems(section);
|
||||
|
||||
if (items.length === 0) return null;
|
||||
@@ -303,13 +344,7 @@ const CustomSummarySection = ({
|
||||
);
|
||||
};
|
||||
|
||||
const ProfileSection = ({
|
||||
sectionId = "profiles",
|
||||
sectionData,
|
||||
}: {
|
||||
sectionId?: string;
|
||||
sectionData?: ItemSection<ProfileItem>;
|
||||
} = {}) => {
|
||||
const ProfileSection = ({ sectionId = "profiles", sectionData }: ItemSectionProps<ProfileItem> = {}) => {
|
||||
const data = useRender();
|
||||
const profiles = sectionData ?? data.sections.profiles;
|
||||
const items = getVisibleItems(profiles, "profiles");
|
||||
@@ -336,13 +371,7 @@ const ProfileSection = ({
|
||||
);
|
||||
};
|
||||
|
||||
const ExperienceSection = ({
|
||||
sectionId = "experience",
|
||||
sectionData,
|
||||
}: {
|
||||
sectionId?: string;
|
||||
sectionData?: ItemSection<ExperienceItem>;
|
||||
} = {}) => {
|
||||
const ExperienceSection = ({ sectionId = "experience", sectionData }: ItemSectionProps<ExperienceItem> = {}) => {
|
||||
const data = useRender();
|
||||
const experience = sectionData ?? data.sections.experience;
|
||||
const items = getVisibleItems(experience, "experience");
|
||||
@@ -422,13 +451,7 @@ const ExperienceSection = ({
|
||||
);
|
||||
};
|
||||
|
||||
const EducationSection = ({
|
||||
sectionId = "education",
|
||||
sectionData,
|
||||
}: {
|
||||
sectionId?: string;
|
||||
sectionData?: ItemSection<EducationItem>;
|
||||
} = {}) => {
|
||||
const EducationSection = ({ sectionId = "education", sectionData }: ItemSectionProps<EducationItem> = {}) => {
|
||||
const data = useRender();
|
||||
const education = sectionData ?? data.sections.education;
|
||||
const items = getVisibleItems(education, "education");
|
||||
@@ -506,13 +529,7 @@ const EducationSection = ({
|
||||
);
|
||||
};
|
||||
|
||||
const ProjectsSection = ({
|
||||
sectionId = "projects",
|
||||
sectionData,
|
||||
}: {
|
||||
sectionId?: string;
|
||||
sectionData?: ItemSection<ProjectItem>;
|
||||
} = {}) => {
|
||||
const ProjectsSection = ({ sectionId = "projects", sectionData }: ItemSectionProps<ProjectItem> = {}) => {
|
||||
const data = useRender();
|
||||
const projects = sectionData ?? data.sections.projects;
|
||||
const items = getVisibleItems(projects, "projects");
|
||||
@@ -543,13 +560,7 @@ const ProjectsSection = ({
|
||||
);
|
||||
};
|
||||
|
||||
const SkillsSection = ({
|
||||
sectionId = "skills",
|
||||
sectionData,
|
||||
}: {
|
||||
sectionId?: string;
|
||||
sectionData?: ItemSection<SkillItem>;
|
||||
} = {}) => {
|
||||
const SkillsSection = ({ sectionId = "skills", sectionData }: ItemSectionProps<SkillItem> = {}) => {
|
||||
const data = useRender();
|
||||
const skills = sectionData ?? data.sections.skills;
|
||||
const items = getVisibleItems(skills, "skills");
|
||||
@@ -583,13 +594,7 @@ const SkillsSection = ({
|
||||
);
|
||||
};
|
||||
|
||||
const LanguagesSection = ({
|
||||
sectionId = "languages",
|
||||
sectionData,
|
||||
}: {
|
||||
sectionId?: string;
|
||||
sectionData?: ItemSection<LanguageItem>;
|
||||
} = {}) => {
|
||||
const LanguagesSection = ({ sectionId = "languages", sectionData }: ItemSectionProps<LanguageItem> = {}) => {
|
||||
const data = useRender();
|
||||
const languages = sectionData ?? data.sections.languages;
|
||||
const items = getVisibleItems(languages, "languages");
|
||||
@@ -613,13 +618,7 @@ const LanguagesSection = ({
|
||||
);
|
||||
};
|
||||
|
||||
const InterestsSection = ({
|
||||
sectionId = "interests",
|
||||
sectionData,
|
||||
}: {
|
||||
sectionId?: string;
|
||||
sectionData?: ItemSection<InterestItem>;
|
||||
} = {}) => {
|
||||
const InterestsSection = ({ sectionId = "interests", sectionData }: ItemSectionProps<InterestItem> = {}) => {
|
||||
const data = useRender();
|
||||
const interests = sectionData ?? data.sections.interests;
|
||||
const items = getVisibleItems(interests, "interests");
|
||||
@@ -647,13 +646,7 @@ const InterestsSection = ({
|
||||
);
|
||||
};
|
||||
|
||||
const AwardsSection = ({
|
||||
sectionId = "awards",
|
||||
sectionData,
|
||||
}: {
|
||||
sectionId?: string;
|
||||
sectionData?: ItemSection<AwardItem>;
|
||||
} = {}) => {
|
||||
const AwardsSection = ({ sectionId = "awards", sectionData }: ItemSectionProps<AwardItem> = {}) => {
|
||||
const data = useRender();
|
||||
const awards = sectionData ?? data.sections.awards;
|
||||
const items = getVisibleItems(awards, "awards");
|
||||
@@ -687,10 +680,7 @@ const AwardsSection = ({
|
||||
const CertificationsSection = ({
|
||||
sectionId = "certifications",
|
||||
sectionData,
|
||||
}: {
|
||||
sectionId?: string;
|
||||
sectionData?: ItemSection<CertificationItem>;
|
||||
} = {}) => {
|
||||
}: ItemSectionProps<CertificationItem> = {}) => {
|
||||
const data = useRender();
|
||||
const certifications = sectionData ?? data.sections.certifications;
|
||||
const items = getVisibleItems(certifications, "certifications");
|
||||
@@ -722,13 +712,7 @@ const CertificationsSection = ({
|
||||
);
|
||||
};
|
||||
|
||||
const PublicationsSection = ({
|
||||
sectionId = "publications",
|
||||
sectionData,
|
||||
}: {
|
||||
sectionId?: string;
|
||||
sectionData?: ItemSection<PublicationItem>;
|
||||
} = {}) => {
|
||||
const PublicationsSection = ({ sectionId = "publications", sectionData }: ItemSectionProps<PublicationItem> = {}) => {
|
||||
const data = useRender();
|
||||
const publications = sectionData ?? data.sections.publications;
|
||||
const items = getVisibleItems(publications, "publications");
|
||||
@@ -761,13 +745,7 @@ const PublicationsSection = ({
|
||||
);
|
||||
};
|
||||
|
||||
const VolunteerSection = ({
|
||||
sectionId = "volunteer",
|
||||
sectionData,
|
||||
}: {
|
||||
sectionId?: string;
|
||||
sectionData?: ItemSection<VolunteerItem>;
|
||||
} = {}) => {
|
||||
const VolunteerSection = ({ sectionId = "volunteer", sectionData }: ItemSectionProps<VolunteerItem> = {}) => {
|
||||
const data = useRender();
|
||||
const volunteer = sectionData ?? data.sections.volunteer;
|
||||
const items = getVisibleItems(volunteer, "volunteer");
|
||||
@@ -812,13 +790,7 @@ const VolunteerSection = ({
|
||||
);
|
||||
};
|
||||
|
||||
const ReferencesSection = ({
|
||||
sectionId = "references",
|
||||
sectionData,
|
||||
}: {
|
||||
sectionId?: string;
|
||||
sectionData?: ItemSection<ReferenceItem>;
|
||||
} = {}) => {
|
||||
const ReferencesSection = ({ sectionId = "references", sectionData }: ItemSectionProps<ReferenceItem> = {}) => {
|
||||
const data = useRender();
|
||||
const references = sectionData ?? data.sections.references;
|
||||
const items = getVisibleItems(references, "references");
|
||||
@@ -845,7 +817,7 @@ const ReferencesSection = ({
|
||||
);
|
||||
};
|
||||
|
||||
const CoverLetterSection = ({ section }: { section: CustomItemSection<CoverLetterItem> }) => {
|
||||
const CoverLetterSection = ({ section }: CoverLetterSectionProps) => {
|
||||
const items = getVisibleItems(section);
|
||||
|
||||
if (items.length === 0) return null;
|
||||
@@ -864,7 +836,7 @@ const CoverLetterSection = ({ section }: { section: CustomItemSection<CoverLette
|
||||
);
|
||||
};
|
||||
|
||||
const CustomSection = ({ sectionId, showHeading = true }: { sectionId: string; showHeading?: boolean }) => {
|
||||
const CustomSection = ({ sectionId, showHeading = true }: CustomSectionProps) => {
|
||||
const data = useRender();
|
||||
const customSection = data.customSections.find((section) => section.id === sectionId);
|
||||
|
||||
@@ -917,15 +889,7 @@ const CustomSection = ({ sectionId, showHeading = true }: { sectionId: string; s
|
||||
.exhaustive();
|
||||
};
|
||||
|
||||
export const Section = ({
|
||||
section,
|
||||
placement,
|
||||
showHeading = true,
|
||||
}: {
|
||||
section: string;
|
||||
placement: TemplatePlacement;
|
||||
showHeading?: boolean;
|
||||
}) => {
|
||||
export const Section = ({ section, placement, showHeading = true }: SectionProps) => {
|
||||
const data = useRender();
|
||||
|
||||
if (!isSectionVisible(section, data)) return null;
|
||||
|
||||
Reference in New Issue
Block a user