|
| 1 | +// Adapted from https://github.com/joe-bell/cva/blob/main/packages/class-variance-authority/src/index.ts |
| 2 | +// - removed clsx dep |
| 3 | +// - removed className prop |
| 4 | +// - use Qwik ClassList instead of ClassValue |
| 5 | +// - simplified VariantProps + expand props in hover tooltip |
| 6 | +// - added VariantPropsFor type |
| 7 | + |
| 8 | +import { type ClassList, type QwikIntrinsicElements } from '@builder.io/qwik'; |
| 9 | + |
| 10 | +/** All the props that the variant function adds, including a `class` */ |
| 11 | +export type VariantProps<VariantFn> = VariantFn extends (p: infer Props) => ClassList |
| 12 | + ? // Expand the props so they are visible in hover tooltips |
| 13 | + { [p in keyof Props]: Props[p] } |
| 14 | + : never; |
| 15 | +/** The DOM props of the given element + the variant props */ |
| 16 | +export type AddVariantPropsTo<El extends keyof QwikIntrinsicElements, VariantFn> = Omit< |
| 17 | + QwikIntrinsicElements[El], |
| 18 | + 'class' |
| 19 | +> & |
| 20 | + VariantProps<VariantFn>; |
| 21 | + |
| 22 | +type ClassProp = { class?: ClassList }; |
| 23 | +type StringToBoolean<T> = T extends 'true' | 'false' ? boolean : T; |
| 24 | + |
| 25 | +const falsyToString = <T>(value: T) => |
| 26 | + typeof value === 'boolean' ? `${value}` : value === 0 ? '0' : value; |
| 27 | + |
| 28 | +type ConfigSchema = { |
| 29 | + [configOption: string]: { [variant: string]: ClassList }; |
| 30 | +}; |
| 31 | +type ConfigVariants<T extends ConfigSchema> = { |
| 32 | + [Variant in keyof T]?: StringToBoolean<keyof T[Variant]> | null | undefined; |
| 33 | +}; |
| 34 | +type ConfigVariantsMulti<T extends ConfigSchema> = { |
| 35 | + [Variant in keyof T]?: |
| 36 | + | StringToBoolean<keyof T[Variant]> |
| 37 | + | StringToBoolean<keyof T[Variant]>[] |
| 38 | + | undefined; |
| 39 | +}; |
| 40 | + |
| 41 | +type Config<T> = T extends ConfigSchema |
| 42 | + ? { |
| 43 | + variants?: T; |
| 44 | + defaultVariants?: ConfigVariants<T>; |
| 45 | + compoundVariants?: (T extends ConfigSchema |
| 46 | + ? (ConfigVariants<T> | ConfigVariantsMulti<T>) & ClassProp |
| 47 | + : ClassProp)[]; |
| 48 | + } |
| 49 | + : never; |
| 50 | + |
| 51 | +type Props<T> = T extends ConfigSchema ? ConfigVariants<T> & ClassProp : ClassProp; |
| 52 | + |
| 53 | +export const cva = |
| 54 | + <T>(base?: ClassList, config?: Config<T>): ((props: Props<T>) => ClassList) => |
| 55 | + (props?: Props<T>) => { |
| 56 | + if (!config?.variants) return [base, props?.class]; |
| 57 | + |
| 58 | + const { variants, defaultVariants } = config; |
| 59 | + |
| 60 | + const getVariantClassNames = Object.keys(variants).map( |
| 61 | + (variant: keyof typeof variants) => { |
| 62 | + const variantProp = props?.[variant as keyof typeof props]; |
| 63 | + const defaultVariantProp = defaultVariants?.[variant]; |
| 64 | + |
| 65 | + if (variantProp === null) return null; |
| 66 | + |
| 67 | + const variantKey = (falsyToString(variantProp) || |
| 68 | + falsyToString(defaultVariantProp)) as keyof (typeof variants)[typeof variant]; |
| 69 | + |
| 70 | + return variants[variant][variantKey]; |
| 71 | + } |
| 72 | + ); |
| 73 | + |
| 74 | + let getCompoundVariantClassNames; |
| 75 | + if (config?.compoundVariants) { |
| 76 | + const combinedProps = props |
| 77 | + ? Object.entries(props).reduce( |
| 78 | + (acc, [key, value]) => { |
| 79 | + if (value === undefined) { |
| 80 | + return acc; |
| 81 | + } |
| 82 | + |
| 83 | + acc[key] = value; |
| 84 | + return acc; |
| 85 | + }, |
| 86 | + { ...defaultVariants } as Record<string, unknown> |
| 87 | + ) |
| 88 | + : defaultVariants; |
| 89 | + |
| 90 | + getCompoundVariantClassNames = |
| 91 | + combinedProps && |
| 92 | + config.compoundVariants.reduce( |
| 93 | + (acc, { class: cvClass, ...compoundVariantOptions }) => |
| 94 | + Object.entries(compoundVariantOptions).every(([key, value]) => { |
| 95 | + const propVal = combinedProps[key]; |
| 96 | + return Array.isArray(value) ? value.includes(propVal) : propVal === value; |
| 97 | + }) |
| 98 | + ? [...acc, cvClass] |
| 99 | + : acc, |
| 100 | + [] as ClassList[] |
| 101 | + ); |
| 102 | + } |
| 103 | + |
| 104 | + return [base, getVariantClassNames, getCompoundVariantClassNames, props?.class]; |
| 105 | + }; |
0 commit comments