native-variants
Version:
A library for handling variants in React Native components with theme support.
356 lines (355 loc) • 11.6 kB
TypeScript
import type { FlexStyle, ImageStyle, TextStyle, TransformsStyle, ViewStyle } from "react-native";
/**
* Combined style type that includes all React Native style properties.
* Supports View, Text, Image, Flex, and Transform styles.
*/
export type Styles = Partial<ViewStyle & TextStyle & ImageStyle & FlexStyle & TransformsStyle>;
/**
* Base styles configuration for slots.
* Maps slot names to their corresponding styles.
*
* @template S - Union type of slot names
*/
export type Base<S extends string> = Partial<Record<S, Styles>>;
/**
* Helper type to convert string literal "true" | "false" to actual boolean type.
* This allows users to use boolean values directly instead of string literals.
*
* @template T - The type to potentially convert
*/
type StringToBoolean<T> = T extends "true" | "false" ? boolean : T;
/**
* Extract variant keys and convert boolean string literals to actual booleans.
*
* @template V - The variants object type
* @template K - The key of the variant
*/
type VariantValue<V, K extends keyof V> = V[K] extends Record<string, unknown> ? StringToBoolean<keyof V[K]> : never;
/**
* Default variants configuration.
* Allows setting default values for each variant, supporting both
* string literals and boolean values for true/false variants.
*
* @template S - Union type of slot names
* @template V - Variants configuration type
*/
export type DefaultVariants<S extends string, V extends Variants<S>> = {
[K in keyof V]?: VariantValue<V, K>;
};
/**
* Compound variant configuration.
* Allows defining styles that apply when multiple variant conditions are met.
*
* @template S - Union type of slot names
* @template V - Variants configuration type
*
* @property css - Optional styles to apply when conditions match
*/
export type CompoundVariant<S extends string, V extends Variants<S>> = {
css?: Partial<Record<S, Styles>>;
} & {
[K in keyof V]?: VariantValue<V, K>;
};
/**
* Variants configuration type.
* Defines the structure for variant definitions with nested slot styles.
*
* @template S - Union type of slot names
*/
export type Variants<S extends string> = {
[K in string]?: {
[K in string]: {
[key in S]?: Styles;
};
};
};
/**
* Mapped variants type for external consumption.
* Used when extracting variant props from a styled component.
*
* @template V - Variants configuration type
*/
export type MappedVariants<V> = Partial<{
[K in keyof V]: V[K] extends Record<string, unknown> ? StringToBoolean<keyof V[K]> : never;
}>;
/**
* Define config function type.
* Helper function that provides type inference for config objects.
*
* @template S - Union type of slot names
* @template V - Variants configuration type
*/
export type DefineConfig = <const S extends string, V extends Variants<S>>(config: Config<S, V>) => Config<S, V>;
/**
* Main configuration type for styled components.
* Defines the complete structure for a styled component configuration.
*
* @template S - Union type of slot names
* @template V - Variants configuration type
*
* @property slots - Array of slot names
* @property base - Optional base styles for each slot
* @property variants - Optional variant definitions
* @property defaultVariants - Optional default variant values
* @property compoundVariants - Optional compound variant conditions
*/
export type Config<S extends string, V extends Variants<S>> = {
slots: S[];
base?: Base<S>;
variants?: V;
defaultVariants?: DefaultVariants<S, V>;
compoundVariants?: CompoundVariant<S, V>[];
};
/**
* Extract variant props from a styled function.
* Useful for creating typed component props based on variant definitions.
*
* @template T - The styled function type
*
* @example
* ```ts
* const buttonVariants = styled({ ... });
* type ButtonProps = VariantProps<typeof buttonVariants>;
* ```
*/
export type VariantProps<T extends (...args: any[]) => any> = T extends (props?: infer P) => any ? Partial<P> : never;
/**
* Color scheme configuration with default (light) and dark variants.
* Both variants must have exactly the same keys for type safety.
*
* @template T - The color keys type
*
* @example
* ```ts
* // TypeScript will error if dark is missing keys from default or vice versa
* const colors = {
* default: { primary: "#000", background: "#fff" },
* dark: { primary: "#fff", background: "#000" } // Must have same keys!
* };
* ```
*/
export type ColorSchemeConfig<T extends Record<string, string>> = {
/** Light theme colors (default) */
default: T;
/** Dark theme colors - must have exactly the same keys as default */
dark: T;
};
/**
* Input type for colors in createNVA theme.
* Ensures both default and dark have identical keys.
*
* @template D - Default colors type
* @template K - Dark colors type (must match default keys)
*/
export type ColorsInput<D extends Record<string, string>> = {
/** Light theme colors (default) */
default: D;
/** Dark theme colors - must have exactly the same keys as default */
dark: {
[K in keyof D]: string;
};
};
/**
* Strict colors input that validates both directions.
* Use this when you want TypeScript to error if either side is missing keys.
*/
export type StrictColorsInput<D extends Record<string, string>, K extends Record<string, string>> = [keyof D] extends [keyof K] ? [keyof K] extends [keyof D] ? {
default: D;
dark: K;
} : {
default: D;
dark: "Error: dark is missing keys from default";
} : {
default: "Error: default is missing keys from dark";
dark: K;
};
/**
* Theme input configuration for createNVA.
* Colors support light/dark mode via default/dark keys.
*
* @template C - Custom colors type (inferred from colors.default)
* @template S - Spacing type
* @template F - Font sizes type
* @template R - Border radii type
* @template T - Shadows type
* @template Z - Z-index type
* @template O - Opacity type
* @template L - Line heights type
*/
export type ThemeInput<C extends Record<string, string> = Record<string, string>, S = any, F = any, R = any, T = any, Z = any, O = any, L = any> = {
/** Color scheme with default (light) and dark variants */
colors?: ColorsInput<C>;
/** Spacing scale tokens */
spacing?: S;
/** Font size scale tokens */
fontSizes?: F;
/** Border radius scale tokens */
radii?: R;
/** Shadow definition tokens */
shadows?: T;
/** Z-index scale tokens */
zIndex?: Z;
/** Opacity scale tokens */
opacity?: O;
/** Line height scale tokens */
lineHeights?: L;
};
/**
* Resolved theme output from createNVA.
* Colors are flattened (default scheme is used directly).
*
* @template C - Custom colors type
* @template S - Spacing type
* @template F - Font sizes type
* @template R - Border radii type
* @template T - Shadows type
* @template Z - Z-index type
* @template O - Opacity type
* @template L - Line heights type
*/
export type ThemeOutput<C extends Record<string, string> = Record<string, string>, S = any, F = any, R = any, T = any, Z = any, O = any, L = any> = {
/** Flattened colors (uses default/light scheme) */
colors: C;
/** Spacing scale tokens */
spacing: S;
/** Font size scale tokens */
fontSizes: F;
/** Border radius scale tokens */
radii: R;
/** Shadow definition tokens */
shadows: T;
/** Z-index scale tokens */
zIndex: Z;
/** Opacity scale tokens */
opacity: O;
/** Line height scale tokens */
lineHeights: L;
};
/**
* Legacy Theme type for backwards compatibility.
* @deprecated Use ThemeInput or ThemeOutput instead
*/
export type Theme<C = any, S = any, F = any, R = any, T = any, Z = any, O = any, L = any> = {
colors?: C;
spacing?: S;
fontSizes?: F;
radii?: R;
shadows?: T;
zIndex?: Z;
opacity?: O;
lineHeights?: L;
};
/**
* Utility function type that takes a value and returns style properties.
* The value type is inferred from React Native style property values.
*
* @template V - The value type (inferred from usage)
*
* @example
* ```ts
* // Simple util
* const mx: UtilFunction<number> = (value) => ({
* marginLeft: value,
* marginRight: value,
* });
* ```
*/
export type UtilFunction<V = any> = (value: V) => Styles;
/**
* Utils configuration object.
* Maps util names to their corresponding functions.
*
* @example
* ```ts
* const utils = {
* mx: (value: number) => ({ marginLeft: value, marginRight: value }),
* my: (value: number) => ({ marginTop: value, marginBottom: value }),
* px: (value: number) => ({ paddingLeft: value, paddingRight: value }),
* py: (value: number) => ({ paddingTop: value, paddingBottom: value }),
* size: (value: number) => ({ width: value, height: value }),
* };
* ```
*/
export type UtilsConfig = Record<string, UtilFunction>;
/**
* Extract the parameter type from a util function.
*/
export type UtilParamType<T> = T extends (value: infer V) => any ? V : never;
/**
* Style properties with utils applied.
* Combines regular styles with util-based style shortcuts.
*
* @template U - Utils configuration type
*
* @example
* ```ts
* // With utils: { mx: (v) => ({...}) }
* // You can use: { mx: 10 } instead of { marginLeft: 10, marginRight: 10 }
* ```
*/
export type StylesWithUtils<U extends UtilsConfig> = Styles & {
[K in keyof U]?: UtilParamType<U[K]>;
};
/**
* Base styles configuration with utils support.
* Maps slot names to their corresponding styles including utils.
*
* @template S - Union type of slot names
* @template U - Utils configuration type
*/
export type BaseWithUtils<S extends string, U extends UtilsConfig> = {
[K in S]?: StylesWithUtils<U>;
};
/**
* Variants configuration type with utils support.
* Preserves the variant keys for proper type inference.
*
* @template S - Union type of slot names
* @template U - Utils configuration type
*/
export type VariantsWithUtils<S extends string, U extends UtilsConfig> = {
[VariantName in string]?: {
[VariantValue in string]?: {
[Slot in S]?: StylesWithUtils<U>;
};
};
};
/**
* Compound variant configuration with utils support.
*
* @template S - Union type of slot names
* @template V - Variants configuration type
* @template U - Utils configuration type
*/
export type CompoundVariantWithUtils<S extends string, V extends VariantsWithUtils<S, U>, U extends UtilsConfig> = {
css?: {
[K in S]?: StylesWithUtils<U>;
};
} & {
[K in keyof V]?: V[K] extends Record<string, unknown> ? StringToBoolean<keyof V[K]> : never;
};
/**
* Default variants for config with utils.
*
* @template S - Union type of slot names
* @template V - Variants configuration type
* @template U - Utils configuration type
*/
export type DefaultVariantsWithUtils<S extends string, V extends VariantsWithUtils<S, U>, U extends UtilsConfig> = {
[K in keyof V]?: V[K] extends Record<string, unknown> ? StringToBoolean<keyof V[K]> : never;
};
/**
* Config type with utils support.
*
* @template S - Union type of slot names
* @template V - Variants configuration type
* @template U - Utils configuration type
*/
export type ConfigWithUtils<S extends string, V extends VariantsWithUtils<S, U>, U extends UtilsConfig> = {
slots: readonly S[] | S[];
base?: BaseWithUtils<S, U>;
variants?: V;
defaultVariants?: DefaultVariantsWithUtils<S, V, U>;
compoundVariants?: CompoundVariantWithUtils<S, V, U>[];
};
export {};