Files
Reactive-Resume/packages/pdf/src/templates/shared/context.tsx
T
Amruth Pillai a325232a09 refactor(pdf): render every resume with semantic css and drop the legacy styler
Now that the API hands out every resume with its legacy style rules
converted, the renderer runs one styling system: the stylesheet mode,
the per-slot legacy rule hooks in the template primitives and the
section style provider are gone, and the Custom Styles editor loses its
legacy draft banner and Activate step. The converter stays (the API
uses it), with its fixtures now checked by rendering the converted
stylesheets.
2026-09-29 15:25:44 +02:00

132 lines
3.8 KiB
TypeScript

import type { ReactNode } from "react";
import type { StyleInput, TemplatePlacement } from "./styles";
import type {
SectionTimelineStyleSlots,
TemplateColorRoles,
TemplateFeatureStyleSlots,
TemplateFeatures,
TemplateIconProps,
TemplateIconSlot,
TemplateStyleSlot,
TemplateStyleSlots,
} from "./types";
import { createContext, use, useMemo } from "react";
type TemplateContextValue = {
styles: TemplateStyleSlots;
featureStyles: TemplateFeatureStyleSlots;
colors: TemplateColorRoles;
features: TemplateFeatures;
pageNodeKey: string;
};
type TemplateProviderProps = Omit<TemplateContextValue, "featureStyles" | "features" | "sectionTitleFallbacks"> & {
featureStyles?: TemplateFeatureStyleSlots;
features?: TemplateFeatures;
pageNodeKey: string;
children: ReactNode;
};
type TemplatePlacementProviderProps = {
placement: TemplatePlacement;
children: ReactNode;
};
const TemplateContext = createContext<TemplateContextValue | null>(null);
const TemplatePlacementContext = createContext<TemplatePlacement>("main");
const resolveStyleSlot = (slot: TemplateStyleSlot | undefined, context: TemplateStyleContextValue): StyleInput => {
if (!slot) return undefined;
if (typeof slot === "function") return slot(context);
return slot;
};
const resolveIconSlot = (
slot: TemplateIconSlot | undefined,
context: TemplateStyleContextValue,
): Partial<TemplateIconProps> => {
if (!slot) return {};
if (typeof slot === "function") return slot(context);
return slot;
};
type TemplateStyleContextValue = {
placement: TemplatePlacement;
colors: TemplateColorRoles;
};
const EMPTY_FEATURE_STYLES: TemplateFeatureStyleSlots = {};
const EMPTY_FEATURES: TemplateFeatures = {};
export const TemplateProvider = ({
styles,
featureStyles = EMPTY_FEATURE_STYLES,
colors,
features = EMPTY_FEATURES,
pageNodeKey,
children,
}: TemplateProviderProps) => {
const contextValue = useMemo<TemplateContextValue>(
() => ({ styles, featureStyles, colors, features, pageNodeKey }),
[colors, featureStyles, features, pageNodeKey, styles],
);
return <TemplateContext.Provider value={contextValue}>{children}</TemplateContext.Provider>;
};
export const TemplatePlacementProvider = ({ placement, children }: TemplatePlacementProviderProps) => {
return <TemplatePlacementContext.Provider value={placement}>{children}</TemplatePlacementContext.Provider>;
};
const useTemplateContext = () => {
const context = use(TemplateContext);
if (!context) throw new Error("useTemplateContext must be called inside a <TemplateProvider>.");
return context;
};
export const useTemplateFeature = (feature: keyof TemplateFeatures): boolean => {
const { features } = useTemplateContext();
return features[feature] ?? false;
};
export const useTemplatePlacement = () => use(TemplatePlacementContext);
export const useTemplatePageNodeKey = () => useTemplateContext().pageNodeKey;
const useTemplateStyleContext = (): TemplateStyleContextValue => {
const { colors } = useTemplateContext();
const placement = useTemplatePlacement();
return { placement, colors };
};
export const useTemplateStyle = (slot: keyof TemplateStyleSlots): StyleInput => {
const { styles } = useTemplateContext();
const context = useTemplateStyleContext();
return resolveStyleSlot(styles[slot] as TemplateStyleSlot | undefined, context);
};
export const useTemplateFeatureStyle = (
feature: keyof TemplateFeatureStyleSlots,
slot: keyof SectionTimelineStyleSlots,
): StyleInput => {
const { featureStyles } = useTemplateContext();
const context = useTemplateStyleContext();
const slots = featureStyles[feature];
return resolveStyleSlot(slots?.[slot] as TemplateStyleSlot | undefined, context);
};
export const useTemplateIconSlot = (slot: "icon" | "sectionHeadingIcon") => {
const { styles } = useTemplateContext();
const context = useTemplateStyleContext();
return resolveIconSlot(styles[slot] as TemplateIconSlot | undefined, context);
};