@gentleduck/variants
Version:
A package for creating variants of components, providing a simple and efficient way to create variants of components.
92 lines • 4.89 kB
JavaScript
/**
* Build a stable cache key by serializing props entries in sorted order.
*
* @template TVariants
* The mapping of variant names to their allowed string/string[] classes.
*
* @param {CvaProps<TVariants>} props
* The props object passed into the CVA function (variant selections + class/className).
*
* @returns {string}
* A deterministic string key used for memoization.
*
* @example
* ```ts
* getCacheKey({ intent: 'primary', size: ['sm', 'md'], className: 'mt-4' })
* // => "className:mt-4|intent:primary|size:[sm,md]"
* ```
*/function e(e){let t=Object.entries(e),n=``;for(let e=0;e<t.length;e++){let[r,i]=t[e];Array.isArray(i)?n+=`${r}:[${i.map(String).join(`,`)}]`:n+=`${r}:${String(i)}`,e<t.length-1&&(n+=`|`)}return n}
/**
* Recursively flattens any supported `ClassValue` into individual CSS tokens.
*
* Supports:
* - primitive strings/numbers/booleans (whitespace-split)
* - nested arrays of `ClassValue`
* - dictionaries `{ className: boolean }` for conditional classes
*
* @param {ClassValue | undefined} input
* The value to flatten into tokens.
* @param {string[]} tokens
* The accumulator array receiving each CSS token.
*
* @example
* ```ts
* const out: string[] = []
* flattenClasses(
* [
* 'px-4 py-2',
* { 'text-bold': true, invisible: false },
* ['hover:bg-red-500', ['active:scale-95']],
* ],
* out
* )
* // out => ['px-4','py-2','text-bold','hover:bg-red-500','active:scale-95']
* ```
*/function t(e,n){if(e!=null){if(typeof e==`string`||typeof e==`number`||typeof e==`boolean`){let t=String(e).split(/\s+/);for(let e=0;e<t.length;e++){let r=t[e];r&&n.push(r)}return}if(Array.isArray(e)){for(let r=0;r<e.length;r++)t(e[r],n);return}for(let t in e)Object.prototype.hasOwnProperty.call(e,t)&&e[t]&&n.push(t)}}
/**
* Creates a Class Variance Authority (CVA) function for composing class names
* based on a base string, variants, defaultVariants, and compoundVariants.
*
* Supports two call signatures:
* - `cva(base: string, options: VariantsOptions<TVariants>)`
* - `cva(options: VariantsOptions<TVariants> & { base: string })`
*
* @template TVariants
* A record mapping variant keys to a record of allowed values and their classes.
*
* @param {string | (VariantsOptions<TVariants> & { base?: string })} baseOrOptions
* Either the base class string, or an options object including `base`.
* @param {VariantsOptions<TVariants>} [maybeOptions]
* The options object when using the two-arg signature.
*
* @returns {(props?: CvaProps<TVariants>) => string}
* A function that, given variant props and optional `class`/`className`, returns
* the deduplicated, memoized className string.
*
* @example
* ```ts
* const button = cva('btn px-4 py-2', {
* variants: {
* intent: { primary: 'bg-blue-500 text-white', danger: 'bg-red-500' },
* size: { sm: 'text-sm', lg: 'text-lg' },
* },
* defaultVariants: { intent: 'primary', size: 'sm' },
* compoundVariants: [
* {
* intent: ['primary','danger'],
* size: 'lg',
* className: 'uppercase',
* },
* ],
* })
*
* // uses defaults + compound match
* button()
* // => 'btn px-4 py-2 bg-blue-500 text-white text-sm uppercase'
*
* // overrides size + adds custom classes
* button({ size: 'lg', class: ['mt-4','shadow'] })
* // => 'btn px-4 py-2 bg-blue-500 text-white text-lg uppercase mt-4 shadow'
* ```
*/function n(n,r){let i=typeof n==`string`?{base:n,...r}:n,{base:a=``,variants:o,defaultVariants:s={},compoundVariants:c=[]}=i,l=new Map;return function(n={}){let r=e(n),i=l.get(r);if(i)return i;let u=[],d=new Set;t(a,u);let f={...s,...n};for(let e in o){let n=f[e];if(n==null||n===`unset`)continue;let r=o[e][String(n)];t(r,u)}for(let e=0;e<c.length;e++){let n=c[e],r=!0;for(let e in n){if(e===`class`||e===`className`)continue;let t=n[e],i=f[e];if(Array.isArray(t)&&i){if(!t.includes(i.toString())){r=!1;break}}else if(i!==t){r=!1;break}}r&&(t(n.class,u),t(n.className,u))}t(n.className,u),t(n.class,u);for(let e=0;e<u.length;e++)d.add(u[e]);let p=Array.from(d).join(` `);return l.set(r,p),p}}function r(e={sm:`p-2 h-2 m-2`,md:`p-4 h-4 m-4`,lg:`p-6 h-6 m-6`}){return Object.entries(e).map(([e,t])=>t.split(` `).map(t=>`${e}:${t}`).join(` `)).join(` `)}const i=n(`flex items-center`,{variants:{variant:{default:`bg-primary text-primary-foreground hover:bg-primary/90 shadow-sm`,destructive:`bg-destructive/90 hover:bg-destructive/70 text-destructive-foreground shadow-xs`,nothing:``},size:{xs:`h-6 px-2 py-1 text-xs [&_svg]:size-[1.3em] rounded-sm`,sm:`h-8 px-3 py-1.5 text-sm [&_svg]:size-[1.3em] rounded-md`,default:`h-9 px-4 py-2 text-base [&_svg]:size-[1.3em] rounded-md`+r()},border:{default:``,primary:`border border-border/40 hover:border-border/80`}},defaultVariants:{variant:`default`,size:`default`,border:`default`}});console.log(i());export{i as buttonVariants,n as cva,r as generateResponsiveVariants};
//# sourceMappingURL=index.js.map