import clsx from "clsx"; import { createContext, forwardRef, useContext } from "react"; type SlotRecipe< Props extends Record, Classnames extends Record, > = ((props?: Props) => Classnames) & { splitVariantProps: (props: T) => [Props, Omit]; }; export function createSlotRecipeContext< Props extends Record, Classnames extends Record, >(recipe: SlotRecipe) { const ClassNamesContext = createContext(null); const PropsContext = createContext(null); const ClassNamesProvider = ({ children, value, }: { children: React.ReactNode; value: Classnames; }) => { return {children}; }; const PropsProvider = ({ children, value }: { children: React.ReactNode; value: Props }) => { return {children}; }; function useClassNames() { const context = useContext(ClassNamesContext); if (context === null) { throw new Error( "useClassNames must be used within a ClassNamesProvider. Did you forget to wrap your component in a ClassNamesProvider?", ); } return context; } function useProps() { return useContext(PropsContext); } const withRootProvider = ( Component: React.ElementType, options?: { defaultProps?: Partial

; }, ): React.ForwardRefExoticComponent> => { const { defaultProps } = options ?? {}; const StyledComponent = (innerProps: any) => { const props = { ...(defaultProps ?? {}), ...useProps(), ...innerProps } as Props & React.HTMLAttributes; const [variantProps, otherProps] = recipe.splitVariantProps(props); const classNames = recipe(variantProps); // TODO: should we memoize this? return ( ); }; // @ts-ignore StyledComponent.displayName = Component.displayName || Component.name; return StyledComponent as any; }; const withProvider = ( Component: React.ElementType, slot: keyof Classnames, options?: { defaultProps?: Partial

; }, ): React.ForwardRefExoticComponent & React.RefAttributes> => { const { defaultProps } = options ?? {}; const StyledComponent = forwardRef((innerProps, ref) => { const props = { ...(defaultProps ?? {}), ...useProps(), ...innerProps } as Props & React.HTMLAttributes; const [variantProps, otherProps] = recipe.splitVariantProps(props); const classNames = recipe(variantProps); // TODO: should we memoize this? const className = classNames[slot as keyof typeof classNames]; return ( ); }); // @ts-ignore StyledComponent.displayName = Component.displayName || Component.name; return StyledComponent as any; }; const withContext = ( Component: React.ElementType, slot?: keyof Classnames, ): React.ForwardRefExoticComponent & React.RefAttributes> => { const StyledComponent = forwardRef>((props, ref) => { const classNames = useClassNames(); const className = classNames?.[slot as keyof typeof classNames]; return ; }); // @ts-ignore StyledComponent.displayName = Component.displayName || Component.name; return StyledComponent as any; }; return { ClassNamesProvider, PropsProvider, useClassNames, useProps, withRootProvider, withProvider, withContext, }; }