diff --git a/src/index.d.ts b/src/index.d.ts index 55e0a30..42b58a5 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -180,7 +180,10 @@ export type TVReturnProps< B extends ClassValue, EV extends TVVariants, ES extends TVSlots, + // @ts-expect-error + E extends TVReturnType = undefined, > = { + extend: E; base: B; slots: S; variants: V; @@ -197,13 +200,15 @@ export type TVReturnType< C extends TVConfig, EV extends TVVariants, ES extends TVSlots, + // @ts-expect-error + E extends TVReturnType = undefined, > = { (props?: TVProps): ES extends undefined ? S extends undefined ? string : {[K in TVSlotsWithBase]: (slotProps?: ClassProp) => string} : {[K in TVSlotsWithBase]: (slotProps?: ClassProp) => string}; -} & TVReturnProps; +} & TVReturnProps; export type TV = { < @@ -229,7 +234,7 @@ export type TV = { >( options: { /** - * Extend allows easily compose components. + * Extend allows for easy composition of components. * @see https://www.tailwind-variants.org/docs/composing-components */ extend?: E; @@ -238,22 +243,22 @@ export type TV = { */ base?: B; /** - * Slots allows you to separate an component into multiple parts. + * Slots allow you to separate a component into multiple parts. * @see https://www.tailwind-variants.org/docs/slots */ slots?: S; /** - * Variants allows you to create multiple versions of the same component. + * Variants allow you to create multiple versions of the same component. * @see https://www.tailwind-variants.org/docs/variants#adding-variants */ variants?: V; /** - * Compound variants allow apply classes to multiple variants at once. + * Compound variants allow you to apply classes to multiple variants at once. * @see https://www.tailwind-variants.org/docs/variants#compound-variants */ compoundVariants?: CV; /** - * Compound slots allow apply classes to multiple slots at once. + * Compound slots allow you to apply classes to multiple slots at once. */ compoundSlots?: TVCompoundSlots; /** @@ -263,11 +268,11 @@ export type TV = { defaultVariants?: DV; }, /** - * The config object to modify the default configuration. + * The config object allows you to modify the default configuration. * @see https://www.tailwind-variants.org/docs/api-reference#config-optional */ config?: C, - ): TVReturnType; + ): TVReturnType; }; // main function diff --git a/src/index.js b/src/index.js index 859745b..2bd6455 100644 --- a/src/index.js +++ b/src/index.js @@ -55,6 +55,7 @@ const joinObjects = (obj1, obj2) => { export const tv = (options, configProp) => { const { + extend = null, slots: slotProps = {}, variants: variantsProps = {}, compoundVariants = [], @@ -64,9 +65,15 @@ export const tv = (options, configProp) => { const config = {...defaultConfig, ...configProp}; - const base = cnBase(options?.extend?.base, options?.base); - const variants = mergeObjects(variantsProps, options?.extend?.variants); - const defaultVariants = {...options?.extend?.defaultVariants, ...defaultVariantsProps}; + const base = extend?.base ? cnBase(extend.base, options?.base) : options?.base; + const variants = + extend?.variants && !isEmptyObject(extend.variants) + ? mergeObjects(variantsProps, extend.variants) + : variantsProps; + const defaultVariants = + extend?.defaultVariants && !isEmptyObject(extend.defaultVariants) + ? {...extend.defaultVariants, ...defaultVariantsProps} + : defaultVariantsProps; // save twMergeConfig to the cache if (!isEmptyObject(config.twMergeConfig) && !isEqual(config.twMergeConfig, cachedTwMergeConfig)) { @@ -83,19 +90,15 @@ export const tv = (options, configProp) => { : {}; // merge slots with the "extended" slots - const slots = isEmptyObject(options?.extend?.slots) + const slots = isEmptyObject(extend?.slots) ? componentSlots : joinObjects( - options?.extend?.slots, + extend?.slots, isEmptyObject(componentSlots) ? {base: options?.base} : componentSlots, ); const component = (props) => { - if ( - isEmptyObject(variants) && - isEmptyObject(slotProps) && - isEmptyObject(options?.extend?.slots) - ) { + if (isEmptyObject(variants) && isEmptyObject(slotProps) && isEmptyObject(extend?.slots)) { return cn(base, props?.class, props?.className)(config); } @@ -259,7 +262,7 @@ export const tv = (options, configProp) => { const getCompoundVariantClassNames = () => { const cvValues = getCompoundVariantsValue(compoundVariants); - const ecvValues = getCompoundVariantsValue(options?.extend?.compoundVariants); + const ecvValues = getCompoundVariantsValue(extend?.compoundVariants); return flatMergeArrays(ecvValues, cvValues); }; @@ -323,7 +326,7 @@ export const tv = (options, configProp) => { }; // with slots - if (!isEmptyObject(slotProps) || !isEmptyObject(options?.extend?.slots)) { + if (!isEmptyObject(slotProps) || !isEmptyObject(extend?.slots)) { const compoundClassNames = getCompoundVariantClassNamesBySlot() ?? []; const compoundSlotClassNames = getCompoundSlotClassNameBySlot() ?? []; @@ -366,6 +369,7 @@ export const tv = (options, configProp) => { }; component.variantKeys = getVariantKeys(); + component.extend = extend; component.base = base; component.slots = slots; component.variants = variants; diff --git a/src/utils.js b/src/utils.js index af3545f..0356394 100644 --- a/src/utils.js +++ b/src/utils.js @@ -7,18 +7,18 @@ export const isEmptyObject = (obj) => export const isEqual = (obj1, obj2) => JSON.stringify(obj1) === JSON.stringify(obj2); function flat(arr, target) { - arr.forEach(function (el) { - if (Array.isArray(el)) flat(el, target); - else target.push(el); - }); + arr.forEach(function (el) { + if (Array.isArray(el)) flat(el, target); + else target.push(el); + }); } export function flatArray(arr) { - const flattened = []; + const flattened = []; - flat(arr, flattened); + flat(arr, flattened); - return flattened; + return flattened; } export const flatMergeArrays = (...arrays) => flatArray(arrays).filter(Boolean);