@pandabox/define-recipe
Version:
End-to-end type-safe theming for PandaCSS
69 lines (63 loc) • 4.73 kB
TypeScript
import { RecipeVariantRecord, RecipeConfig, Pretty, RecipeCompoundVariant, RecipeCompoundSelection, RecipeSelection, DistributiveOmit, SlotRecipeVariantRecord, SlotRecipeConfig } from '@pandacss/types';
export { RecipeCompoundSelection, RecipeCompoundVariant, RecipeConfig, RecipeSelection, RecipeVariantRecord, SlotRecipeConfig, SlotRecipeVariantRecord } from '/types';
type Pick_<T, K> = Pick<T, Extract<keyof T, K>>;
type DistributivePick<T, K> = T extends unknown ? keyof Pick_<T, K> extends never ? never : {
[P in keyof Pick_<T, K>]: Pick_<T, K>[P];
} : never;
interface RecipeBuilder<T extends RecipeVariantRecord> extends RecipeConfig<T> {
extend: <TVariants extends RecipeVariantRecord>(variants: TVariants) => RecipeBuilder<Pretty<T & TVariants>>;
merge: <TVariants extends RecipeVariantRecord, MergedVariants extends Pretty<TVariants & T> = Pretty<TVariants & T>>(extension: Partial<Omit<RecipeConfig<any>, 'variants' | 'compoundVariants' | 'defaultVariants'>> & {
variants?: TVariants;
compoundVariants?: Array<Pretty<RecipeCompoundVariant<RecipeCompoundSelection<MergedVariants>>>>;
defaultVariants?: RecipeSelection<MergedVariants>;
}) => RecipeBuilder<MergedVariants>;
pick: <TKeys extends keyof T>(...keys: TKeys[]) => RecipeBuilder<DistributivePick<T, TKeys>>;
omit: <TKeys extends keyof T>(...keys: TKeys[]) => RecipeBuilder<DistributiveOmit<T, TKeys>>;
cast: () => RecipeConfig<RecipeVariantRecord>;
}
type PickSlots<S extends string, T extends SlotRecipeVariantRecord<S>, TKeys extends S> = Pretty<{
[VName in keyof T]: {
[VKey in keyof T[VName]]: {
[VSlot in Extract<keyof T[VName][VKey], TKeys>]: T[VName][VKey][VSlot];
};
};
}>;
type OmitSlots<S extends string, T extends SlotRecipeVariantRecord<S>, TKeys extends S> = Pretty<{
[VName in keyof T]: {
[VKey in keyof T[VName]]: {
[VSlot in Exclude<keyof T[VName][VKey], TKeys>]: T[VName][VKey][VSlot];
};
};
}>;
interface SlotExtensionFns<S extends string, T extends SlotRecipeVariantRecord<S>> {
add: <TSlots extends string>(...slots: TSlots[]) => SlotRecipeBuilder<S | TSlots, T extends SlotRecipeVariantRecord<S | TSlots> ? T : never>;
pick: <TKeys extends S>(...keys: TKeys[]) => SlotRecipeBuilder<Extract<S, TKeys>, PickSlots<S, T, TKeys>>;
omit: <TKeys extends S>(...keys: TKeys[]) => SlotRecipeBuilder<Exclude<S, TKeys>, OmitSlots<S, T, TKeys>>;
/** Assign simple recipe to slot */
assignTo: <TSlot extends S, TRecipe extends RecipeConfig, TVariants extends NonNullable<TRecipe['variants']> = NonNullable<TRecipe['variants']>>(slot: TSlot, recipe: TRecipe) => SlotRecipeBuilder<S, {
[VName in keyof T]: {
[VKey in keyof T[VName]]: {
[VSlot in keyof T[VName][VKey]]: VSlot extends TSlot ? {
[VRecipeVariant in keyof TVariants]: VRecipeVariant extends VName ? VKey extends keyof TVariants[VRecipeVariant] ? TVariants[VRecipeVariant][VKey] : T[VName][VKey][VSlot] : T[VName][VKey][VSlot];
}[keyof TVariants] : T[VName][VKey][VSlot];
};
};
}>;
}
interface SlotRecipeBuilder<S extends string, T extends SlotRecipeVariantRecord<S>> extends SlotRecipeConfig<S, T> {
extend: <TVariants extends SlotRecipeVariantRecord<S>>(variants: TVariants) => SlotRecipeBuilder<S, Pretty<T & TVariants>>;
merge: <TVariants extends SlotRecipeVariantRecord<S>, MergedVariants extends Pretty<TVariants & T> = Pretty<TVariants & T>>(extension: Partial<Omit<SlotRecipeConfig<any>, 'slots' | 'variants' | 'compoundVariants' | 'defaultVariants'>> & {
slots?: S[];
variants?: TVariants extends unknown ? SlotRecipeVariantRecord<S> : TVariants;
compoundVariants?: Array<Pretty<RecipeCompoundVariant<RecipeCompoundSelection<MergedVariants>>>>;
defaultVariants?: RecipeSelection<MergedVariants>;
}) => SlotRecipeBuilder<S, MergedVariants>;
pick: <TKeys extends keyof T>(...keys: TKeys[]) => SlotRecipeBuilder<S, DistributivePick<T, TKeys>>;
omit: <TKeys extends keyof T>(...keys: TKeys[]) => SlotRecipeBuilder<S, DistributiveOmit<T, TKeys>>;
cast: () => SlotRecipeConfig<S, SlotRecipeVariantRecord<S>>;
/** Add slots, pick or omit some or assign a config recipe to a slot */
slot: SlotExtensionFns<S, T>;
}
declare function defineRecipe<T extends RecipeVariantRecord>(config: RecipeConfig<T>): RecipeBuilder<T>;
declare function defineSlotRecipe<S extends string, T extends SlotRecipeVariantRecord<S>>(config: SlotRecipeConfig<S, T>): SlotRecipeBuilder<S, T>;
export { type RecipeBuilder, type SlotExtensionFns, type SlotRecipeBuilder, defineRecipe, defineSlotRecipe };