UNPKG

css-variants

Version:

Lightweight helpers to compose class names and inline styles using variants. Zero runtime deps, small bundle, and first-class TypeScript support.

1 lines 10.1 kB
{"version":3,"sources":["../src/cx.ts","../src/utils/merge-props.ts","../src/scv.ts"],"names":["toVal","input","result","i","tmpClassValue","tmpClassName","key","cx","args","mergeProps","defaultProps","props","omitKeys","merged","k","scv","config","slots","base","variants","compoundVariants","defaultVariants","classNameResolver","mergedProps","slotClassValues","slot","cls","compound","matches","value","propValue","scv_default"],"mappings":"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","file":"scv.cjs","sourcesContent":["// credit: https://github.com/lukeed/clsx\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type ClassDictionary = Record<string, any>\nexport type ClassValue = ClassValue[] | string | number | bigint | ClassDictionary | null | boolean | undefined\nexport type ClassArray = ClassValue[]\n\nfunction toVal(input: ClassValue): string {\n if (typeof input === 'string') {\n return input\n }\n\n if (typeof input === 'number' || typeof input === 'bigint') {\n return String(input)\n }\n\n if (input === null || input === undefined || typeof input === 'boolean') {\n return ''\n }\n\n let result = ''\n\n if (Array.isArray(input)) {\n let i = 0\n let tmpClassValue: ClassValue\n let tmpClassName: string\n for (; i < input.length; i++) {\n if ((tmpClassValue = input[i])) {\n if ((tmpClassName = toVal(tmpClassValue))) {\n if (result) result += ' '\n result += tmpClassName\n }\n }\n }\n\n return result\n }\n\n for (const key in input) {\n if (input[key]) {\n if (result) result += ' '\n result += key\n }\n }\n\n return result\n}\n\nexport function cx(...args: ClassValue[]): string {\n let result = ''\n let i = 0\n let tmpClassValue: ClassValue\n let tmpClassName: string\n\n for (; i < args.length; i++) {\n if ((tmpClassValue = args[i])) {\n if ((tmpClassName = toVal(tmpClassValue))) {\n if (result) result += ' '\n result += tmpClassName\n }\n }\n }\n\n return result\n}\n\nexport default cx\n","export function mergeProps<T extends Record<string, unknown>, P extends Record<string, unknown>>(\n defaultProps: T | undefined,\n props: P | undefined,\n omitKeys?: (keyof P)[]\n): Record<string, unknown> {\n const merged: Record<string, unknown> = { ...defaultProps }\n\n if (props) {\n for (const k in props) {\n if (props[k] !== undefined && (!omitKeys || !omitKeys.includes(k))) {\n merged[k] = props[k]\n }\n }\n }\n\n return merged\n}\n","import { ObjectKeyPicker, ObjectKeyArrayPicker, PartialRecord } from './utils/types'\nimport { cx, ClassValue } from './cx'\nimport { mergeProps } from './utils/merge-props'\n\nexport type SlotClassRecord<S extends string> = PartialRecord<S, ClassValue>\n\nexport type SlotClassVariantRecord<S extends string> = Record<string, Record<string, SlotClassRecord<S>>>\n\nexport type SlotClassVariantExtendProps<S extends string> = { classNames: SlotClassRecord<S> }\n\nexport interface SlotClassVariantDefinition<S extends string, T extends SlotClassVariantRecord<S> | undefined> {\n slots: S[]\n base?: SlotClassRecord<S>\n variants?: T\n compoundVariants?: (ObjectKeyArrayPicker<T> & SlotClassVariantExtendProps<S>)[]\n defaultVariants?: ObjectKeyPicker<T>\n classNameResolver?: typeof cx\n}\n\nexport type SlotClassVariantFnProps<\n S extends string,\n T extends SlotClassVariantRecord<S> | undefined,\n> = T extends undefined\n ? Partial<SlotClassVariantExtendProps<S>>\n : ObjectKeyPicker<T> & Partial<SlotClassVariantExtendProps<S>>\n\nexport type SlotClassVariantFn<S extends string, T extends SlotClassVariantRecord<S> | undefined> = (\n props?: SlotClassVariantFnProps<S, T>\n) => Record<S, string>\n\nexport type SlotClassVariantCreatorFn = <S extends string, T extends SlotClassVariantRecord<S> | undefined>(\n config: SlotClassVariantDefinition<S, T>\n) => SlotClassVariantFn<S, T>\n\n/**\n * Creates a slot-based class variant function that manages class names for multiple slots with variants.\n *\n * @param config - Configuration object for creating the variant function\n * @returns A function that accepts variant props and returns class names for each slot\n *\n * @example\n * ```typescript\n * const button = scv({\n * slots: ['root', 'icon'],\n * base: {\n * root: 'btn',\n * icon: 'btn-icon'\n * },\n * variants: {\n * size: {\n * sm: {\n * root: 'btn-sm',\n * icon: 'icon-sm'\n * },\n * lg: {\n * root: 'btn-lg',\n * icon: 'icon-lg'\n * }\n * }\n * },\n * defaultVariants: {\n * size: 'sm'\n * }\n * })\n *\n * // Usage\n * const classes = button({ size: 'lg' })\n * // Result: { root: 'btn btn-lg', icon: 'btn-icon icon-lg' }\n * ```\n */\nexport const scv: SlotClassVariantCreatorFn = (config) => {\n const { slots, base, variants, compoundVariants, defaultVariants, classNameResolver = cx } = config\n\n if (!variants) {\n return (props) => {\n const result = {} as Record<(typeof slots)[number], string>\n\n for (const slot of slots) {\n result[slot] = classNameResolver(base?.[slot], props?.classNames?.[slot])\n }\n\n return result\n }\n }\n\n return (props) => {\n const mergedProps = mergeProps(defaultVariants, props, ['classNames'])\n\n const slotClassValues = {} as Record<(typeof slots)[number], ClassValue[]>\n\n for (const slot of slots) {\n if (base?.[slot]) {\n slotClassValues[slot] = Array.isArray(base[slot]) ? [...base[slot]] : [base[slot]]\n } else {\n slotClassValues[slot] = []\n }\n }\n\n for (const key in mergedProps) {\n const cls = variants[key]?.[mergedProps[key] as string]\n\n if (cls) {\n for (const slot in cls) {\n slotClassValues[slot]?.push(cls[slot])\n }\n }\n }\n\n if (compoundVariants) {\n for (let i = 0; i < compoundVariants.length; i++) {\n const compound = compoundVariants[i]\n let matches = true\n for (const key in compound) {\n if (key === 'classNames') continue\n const value = compound[key as keyof typeof compound]\n const propValue = mergedProps[key]\n if (Array.isArray(value) ? !value.includes(propValue) : value !== propValue) {\n matches = false\n break\n }\n }\n if (matches && compound.classNames) {\n for (const slot in compound.classNames) {\n slotClassValues[slot]?.push(compound.classNames[slot])\n }\n }\n }\n }\n\n if (props?.classNames) {\n for (const slot in props.classNames) {\n slotClassValues[slot]?.push(props.classNames[slot])\n }\n }\n\n const result = {} as Record<(typeof slots)[number], string>\n\n for (const slot in slotClassValues) {\n result[slot] = classNameResolver(slotClassValues[slot])\n }\n\n return result\n }\n}\n\nexport default scv\n"]}