import type { StyleSlot } from "@reactive-resume/schema/resume/data"; import type { ReactNode } from "react"; import type { SectionStyleRuleContext } from "./style-rules"; import type { StyleInput, TemplatePlacement } from "./styles"; import type { SectionTimelineStyleSlots, TemplateColorRoles, TemplateFeatureStyleSlots, TemplateFeatures, TemplateIconProps, TemplateIconSlot, TemplateStyleSlot, TemplateStyleSlots, } from "./types"; import { createContext, use, useMemo } from "react"; import { useRender } from "../../context"; import { resolveStyleRuleSlot } from "./style-rules"; type TemplateContextValue = { styles: TemplateStyleSlots; featureStyles: TemplateFeatureStyleSlots; colors: TemplateColorRoles; features: TemplateFeatures; }; type TemplateProviderProps = Omit & { featureStyles?: TemplateFeatureStyleSlots; features?: TemplateFeatures; children: ReactNode; }; const TemplateContext = createContext(null); const TemplatePlacementContext = createContext("main"); const SectionStyleContext = createContext(null); 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 => { 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, children, }: TemplateProviderProps) => { const contextValue = useMemo( () => ({ styles, featureStyles, colors, features }), [colors, featureStyles, features, styles], ); return {children}; }; export const TemplatePlacementProvider = ({ placement, children, }: { placement: TemplatePlacement; children: ReactNode; }) => { return {children}; }; export const SectionStyleProvider = ({ context, children, }: { context: SectionStyleRuleContext; children: ReactNode; }) => { return {children}; }; const useTemplateContext = () => { const context = use(TemplateContext); if (!context) throw new Error("useTemplateContext must be called inside a ."); return context; }; export const useTemplateFeature = (feature: keyof TemplateFeatures): boolean => { const { features } = useTemplateContext(); return features[feature] ?? false; }; export const useTemplatePlacement = () => use(TemplatePlacementContext); 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 useSectionStyleRule = (slot: StyleSlot): StyleInput => { const data = useRender(); const context = use(SectionStyleContext); if (!context) return undefined; return resolveStyleRuleSlot(data, { ...context, slot }); }; 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") => { const { styles } = useTemplateContext(); const context = useTemplateStyleContext(); return resolveIconSlot(styles[slot], context); };