import type { Style } from "@react-pdf/types"; import type { TemplatePageProps } from "../../document"; import type { TemplateColorRoles, TemplateStyleContext, TemplateStyleSlots } from "../shared/types"; import { useMemo } from "react"; import { rgbaStringToHex } from "@reactive-resume/utils/color"; import { Image, Page, StyleSheet, View } from "#react-pdf-renderer"; import { useRender } from "../../context"; import { createBaseTemplateStyles } from "../shared/base-template-styles"; import { CustomFieldContactItem, EmailContactItem, LocationContactItem, PhoneContactItem, WebsiteContactItem, } from "../shared/contact-item"; import { TemplateProvider } from "../shared/context"; import { shouldShowResumeHeader } from "../shared/cover-letter"; import { filterSections } from "../shared/filtering"; import { getTemplateMetrics } from "../shared/metrics"; import { getTemplatePageMinHeightStyle, getTemplatePageSize } from "../shared/page-size"; import { hasTemplatePicture } from "../shared/picture"; import { Heading, Text } from "../shared/primitives"; import { createRtlStyleHelpers } from "../shared/rtl"; import { Section } from "../shared/sections"; import { composeStyles, headerNameLineHeight } from "../shared/styles"; type LaprasStyles = Omit & { page: Style; header: Style; picture: Style; headerTitle: Style; headerIdentity: Style; headerName: Style; contactList: Style; contactItem: Style; sectionGroup: Style; }; type LaprasTemplate = { colors: TemplateColorRoles; styles: LaprasStyles; }; type LaprasHeaderProps = { styles: LaprasStyles; }; export const LaprasPage = ({ page, pageIndex }: TemplatePageProps) => { const data = useRender(); const { metadata } = data; const { colors, styles } = useLaprasTemplate(); const metrics = getTemplateMetrics(metadata.page); const pageSize = getTemplatePageSize(metadata.page.format); const pageMinHeightStyle = getTemplatePageMinHeightStyle(metadata.page.format); const showHeader = shouldShowResumeHeader(data, pageIndex); const mainSections = filterSections(page.main, data); const sidebarSections = filterSections(page.sidebar, data); return ( {showHeader &&
} {mainSections.map((section) => (
))} {!page.fullWidth && ( {sidebarSections.map((section) => (
))} )} ); }; const Header = ({ styles }: LaprasHeaderProps) => { const { basics, picture } = useRender(); const hasPicture = hasTemplatePicture(picture); return ( {hasPicture && } {basics.name} {basics.headline} {basics.customFields.map((field) => ( ))} ); }; const useLaprasTemplate = (): LaprasTemplate => { const { picture, metadata, rtl } = useRender(); return useMemo(() => { const r = createRtlStyleHelpers(rtl); const foreground = rgbaStringToHex(metadata.design.colors.text); const background = rgbaStringToHex(metadata.design.colors.background); const primary = rgbaStringToHex(metadata.design.colors.primary); const borderColor = "#CCCCCC"; const pictureBorderRadius = Math.min(picture.borderRadius, 30); const headingNegativeMargin = metadata.typography.heading.fontSize + 6; const colors: TemplateColorRoles = { foreground, background, primary }; const metrics = getTemplateMetrics(metadata.page); const base = createBaseTemplateStyles({ metadata, foreground, r, metrics, picture }); const baseStyles = StyleSheet.create({ ...base, page: { color: foreground, backgroundColor: background, paddingHorizontal: metrics.page.paddingHorizontal, paddingVertical: metrics.page.paddingVertical, rowGap: metrics.gapY(1.5), fontFamily: metadata.typography.body.fontFamily, fontSize: metadata.typography.body.fontSize, lineHeight: metadata.typography.body.lineHeight, direction: r.pageDirection, }, section: { flexDirection: "column", rowGap: metrics.gapY(0.25), borderWidth: 1, borderColor: borderColor, borderRadius: pictureBorderRadius, backgroundColor: background, padding: metrics.gapX(1), }, sectionHeading: { alignSelf: "flex-start", marginTop: -headingNegativeMargin, backgroundColor: background, paddingHorizontal: metrics.gapX(1), }, item: { rowGap: metrics.gapY(0.125), }, levelContainer: { width: "100%", }, levelItem: { borderColor: primary, }, levelItemActive: { backgroundColor: primary, }, header: { flexDirection: r.row, alignItems: "center", columnGap: metrics.gapX(1), borderWidth: 1, borderColor: borderColor, borderRadius: pictureBorderRadius, backgroundColor: background, padding: metrics.gapX(1), }, headerTitle: { rowGap: metrics.gapY(0.5), }, headerIdentity: { ...r.headerIdentity, rowGap: metrics.gapY(0.35), }, headerName: { fontSize: metadata.typography.heading.fontSize * 1.5, lineHeight: headerNameLineHeight, }, contactList: { flexDirection: r.row, flexWrap: "wrap", rowGap: metrics.gapY(0.125), columnGap: metrics.gapX(0.5), }, contactItem: { flexDirection: r.row, alignItems: "center", columnGap: metrics.gapX(1 / 6), }, sectionGroup: {}, }); const accentFor = ({ colors }: TemplateStyleContext) => colors.primary; return { colors, styles: { ...baseStyles, levelItem: (context) => ({ borderColor: accentFor(context) }), levelItemActive: (context) => ({ backgroundColor: accentFor(context) }), icon: (context) => ({ display: metadata.page.hideIcons ? "none" : "flex", size: metadata.typography.body.fontSize, color: accentFor(context), }), } satisfies LaprasStyles, }; }, [picture, metadata, rtl]); };