cva-extended
Version:
Class Variance Authority 🧬 - Extended
1 lines • 14.6 kB
Source Map (JSON)
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Copyright 2022 Joe Bell. All rights reserved.\n *\n * This file is licensed to you under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with the\n * License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n * WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\nimport { clsx } from \"clsx\";\n\n/* Types\n ============================================ */\n\n/* clsx\n ---------------------------------- */\n\n// When compiling with `declaration: true`, many projects experience the dreaded\n// TS2742 error. To combat this, we copy clsx's types manually.\n// Should this project move to JSDoc, this workaround would no longer be needed.\n\nexport type ClassValue =\n | ClassArray\n | ClassDictionary\n | string\n | number\n | bigint\n | null\n | boolean\n | undefined;\nexport type ClassDictionary = Record<string, any>;\nexport type ClassArray = ClassValue[];\n\n/* Utils\n ---------------------------------- */\n\ntype OmitUndefined<T> = T extends undefined ? never : T;\ntype StringToBoolean<T> = T extends \"true\" | \"false\" ? boolean : T;\ntype UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (\n k: infer I,\n) => void\n ? I\n : never;\ntype AnyFunction = (...args: any[]) => any;\ntype RequiredKeys<O extends object, K extends keyof O> = Omit<O, K> &\n Required<Pick<O, K>>;\ntype RequiredKeysOf<T extends object> = Exclude<\n {\n [Key in keyof T]: T extends Record<Key, T[Key]> ? Key : never;\n }[keyof T],\n undefined\n>;\n\n/**\n * A private variant is a variant that is not meant to be used by the consumer, and is not exposed via `VariantProps`.\n * Any variant that starts with a `$` is considered a private variant.\n */\ntype PrivateVariant<TVariantName extends string = string> = `$${TVariantName}`;\n\nexport type VariantProps<Component extends AnyFunction> = Omit<\n InternalVariantProps<Component>,\n PrivateVariant\n>;\n\ntype InternalVariantProps<Component extends (...args: any) => any> = Omit<\n OmitUndefined<Parameters<Component>[0]>,\n \"class\" | \"className\"\n>;\n\nconst falsyToString = <T extends unknown>(value: T) =>\n typeof value === \"boolean\" ? `${value}` : value === 0 ? \"0\" : value;\n\nconst normalizeVariantKey = <T extends string>(value: T) => {\n if (value === \"true\") return true;\n if (value === \"false\") return false;\n\n const maybeNumber = Number(value);\n if (!Number.isNaN(maybeNumber)) return maybeNumber;\n\n return value;\n};\n\n/* compose\n ---------------------------------- */\n\ntype AnyCVABuilderFunction = AnyFunction & {\n variants: never | Record<string, readonly VariantValue[]>;\n};\nexport interface Compose {\n <T extends AnyCVABuilderFunction[]>(\n ...components: [...T]\n ): OptionalizeParameter<\n (\n props: UnionToIntersection<\n {\n // for every component, get its props\n [K in keyof T]: InternalVariantProps<T[K]>;\n }[number]\n > &\n CVAClassProp,\n ) => string\n > & {\n variants: UnionToIntersection<Exclude<T[number][\"variants\"], never>>;\n };\n}\n\n/* cx\n ---------------------------------- */\n\nexport interface CX {\n (...inputs: ClassValue[]): string;\n}\n\nexport type CXOptions = Parameters<CX>;\nexport type CXReturn = ReturnType<CX>;\n\n/* cva\n ============================================ */\n\ntype CVAConfigBase = { base?: ClassValue };\ntype CVAVariantShape = Record<string, Record<string, ClassValue>>;\ntype CVAVariantSchema<V extends CVAVariantShape> = {\n [Variant in keyof V]?: StringToBoolean<keyof V[Variant]> | undefined;\n};\ntype CVAClassProp =\n | {\n class?: ClassValue;\n className?: never;\n }\n | {\n class?: never;\n className?: ClassValue;\n };\n\ntype CVAVariantSchemaProps<V extends CVAVariantSchema<any>, D> = Omit<\n RequiredKeys<\n V,\n // Everything that doesn't have a default value declared\n Exclude<keyof V, keyof D>\n >,\n PrivateVariant\n>;\n\nexport interface CVA {\n <\n _ extends \"cva's generic parameters are restricted to internal use only.\",\n TVariants,\n TDefaultVariants,\n >(\n config: TVariants extends CVAVariantShape\n ? CVAConfigBase & {\n variants?: TVariants;\n compoundVariants?: (TVariants extends CVAVariantShape\n ? (\n | CVAVariantSchema<TVariants>\n | {\n [Variant in keyof TVariants]?:\n | StringToBoolean<keyof TVariants[Variant]>\n | readonly StringToBoolean<keyof TVariants[Variant]>[]\n | undefined;\n }\n ) &\n CVAClassProp\n : CVAClassProp)[];\n /**\n * The default values that will be used when a variant is not provided.\n * To mark a property as optional without specifying a value, use `undefined`.\n *\n * @example\n * ```ts\n * const box = cva({\n * variants: {\n * border: {\n * true: '...'\n * }\n * },\n * defaultVariants: {\n * border: undefined\n * }\n * })\n * ```\n */\n defaultVariants?: CVAVariantSchema<TVariants> & TDefaultVariants;\n }\n : CVAConfigBase & {\n variants?: never;\n compoundVariants?: never;\n defaultVariants?: never;\n },\n ): OptionalizeParameter<\n (\n props: CVAClassProp &\n (TVariants extends CVAVariantShape\n ? CVAVariantSchemaProps<CVAVariantSchema<TVariants>, TDefaultVariants>\n : {}),\n ) => string\n > & {\n variants: TVariants extends CVAVariantShape\n ? {\n [K in Exclude<keyof TVariants, PrivateVariant>]: ReadonlyArray<\n StringToBoolean<Extract<keyof TVariants[K], VariantValue>>\n >;\n }\n : never;\n };\n}\n\n/**\n * A function that accepts one parameter `T` and returns another parameter `R`.\n */\ntype UnaryFunction<in T, R> = (arg: T) => R;\n\n/**\n * Takes in an unary function and returns the same function, with the first parameter being optional, if the original function's first parameter is an\n * object with all of its keys being optional.\n */\ntype OptionalizeParameter<F extends UnaryFunction<any, any>> =\n RequiredKeysOf<Parameters<F>[0]> extends []\n ? (arg?: Parameters<F>[0]) => ReturnType<F>\n : F;\n\ntype VariantValue = string | number | boolean;\n\n/* defineConfig\n ---------------------------------- */\n\nexport interface DefineConfigOptions {\n hooks?: {\n /**\n * @deprecated please use `onComplete`\n */\n \"cx:done\"?: (className: string) => string;\n /**\n * Returns the completed string of concatenated classes/classNames.\n */\n onComplete?: (className: string) => string;\n };\n}\n\nexport interface DefineConfig {\n (options?: DefineConfigOptions): {\n compose: Compose;\n cx: CX;\n cva: CVA;\n };\n}\n\n/* Exports\n ============================================ */\n\nexport const defineConfig: DefineConfig = (options) => {\n const cx: CX = (...inputs) => {\n if (options?.hooks?.[\"cx:done\"] !== undefined)\n return options?.hooks[\"cx:done\"](clsx(inputs));\n\n if (options?.hooks?.onComplete !== undefined)\n return options?.hooks.onComplete(clsx(inputs));\n\n return clsx(inputs);\n };\n\n const cva: CVA = (config) => {\n const { variants, defaultVariants } = config ?? {};\n\n const cvaClassBuilder = ((props) => {\n if (variants == null)\n return cx(config?.base, props?.class, props?.className);\n\n const getVariantClassNames = Object.keys(variants).map(\n (variant: keyof typeof variants) => {\n const variantProp = props?.[variant as keyof typeof props];\n const defaultVariantProp = defaultVariants?.[variant];\n\n const variantKey = (falsyToString(variantProp) ||\n falsyToString(\n defaultVariantProp,\n )) as keyof (typeof variants)[typeof variant];\n\n return variants[variant]![variantKey];\n },\n );\n\n const defaultsAndProps = {\n ...defaultVariants,\n // remove `undefined` props\n ...(props &&\n Object.entries(props).reduce<typeof props>(\n (acc, [key, value]) => {\n if (value !== undefined) {\n Object.assign(acc, { [key]: value });\n }\n\n return acc;\n },\n {} as typeof props,\n )),\n };\n\n const getCompoundVariantClassNames = config?.compoundVariants?.reduce(\n (acc, { class: cvClass, className: cvClassName, ...cvConfig }) =>\n Object.entries(cvConfig).every(([cvKey, cvSelector]) => {\n const selector =\n defaultsAndProps[cvKey as keyof typeof defaultsAndProps];\n\n return Array.isArray(cvSelector)\n ? cvSelector.includes(selector)\n : selector === cvSelector;\n })\n ? [...acc, cvClass, cvClassName]\n : acc,\n [] as ClassValue[],\n );\n\n return cx(\n config?.base,\n getVariantClassNames,\n getCompoundVariantClassNames,\n props?.class,\n props?.className,\n );\n }) as ReturnType<CVA>;\n\n return Object.assign(cvaClassBuilder, {\n variants: variants\n ? Object.fromEntries(\n Object.entries(variants)\n // filter out private variants\n .filter(([variantKey]) => !variantKey.startsWith(\"$\"))\n .map(([key, value]) => [\n key,\n Object.keys(value).map((propertyKey) =>\n normalizeVariantKey(propertyKey),\n ),\n ]),\n )\n : {},\n });\n };\n\n const compose: Compose = ((...components) => {\n const composeClassBuilder = ((props) => {\n const propsWithoutClass = Object.fromEntries(\n Object.entries(props || {}).filter(\n ([key]) => ![\"class\", \"className\"].includes(key),\n ),\n );\n\n return cx(\n components.map((component) => component(propsWithoutClass)),\n props?.class,\n props?.className,\n );\n }) as ReturnType<Compose>;\n\n return Object.assign(composeClassBuilder, {\n variants: components.reduce(\n (variantsDraft, { variants }) => {\n // eslint-disable-next-line guard-for-in\n for (const variant in variants) {\n // If both have the same values (e.g. size, and both have xsmall, small, ...), you end up with an array like:\n // ['xsmall', 'small', ..., 'xsmall', 'small', ...] instead of just ['xsmall', 'small', ...]\n variantsDraft[variant] = [\n ...new Set([\n ...(variantsDraft[variant] ?? []),\n ...(variants[variant] ?? []),\n ]),\n ];\n }\n\n return variantsDraft;\n },\n {} as {\n [variant: string]: VariantValue[];\n },\n ),\n });\n }) as Compose;\n\n return {\n compose,\n cva,\n cx,\n };\n};\n\nexport const { compose, cva, cx } = defineConfig();\n"]}