UNPKG

jacaranda

Version:

A lightweight styling library for React Native

1 lines 17.7 kB
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * ISC License\n * Copyright 2025 Javier Diaz Chamorro\n *\n * Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby\n * granted, provided that the above copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING\n * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,\n * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,\n * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE\n * OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n// Example of usage:\n// const buttonStyles = style({\n// base: {\n// display: 'flex',\n// },\n// variants: {\n// visual: {\n// solid: { backgroundColor: '#FC8181', color: 'white' },\n// outline: { borderWidth: 1, borderColor: '#FC8181' },\n// },\n// size: {\n// sm: { padding: 4, fontSize: 12 },\n// lg: { padding: 8, fontSize: 24 },\n// },\n// },\n// // Optional: Add compound variants for specific combinations\n// compoundVariants: [\n// {\n// variants: { visual: 'solid', size: ['lg'] },\n// style: { fontWeight: 'bold' },\n// },\n// ],\n// defaultVariants: {\n// visual: 'solid',\n// size: 'sm',\n// }\n// });\n\n// Example of using a styled component with a custom prop\n// const button = styled(Button)<{ otherProp: string }>((props) => {\n// position: props.otherProp ? 'absolute' : 'relative';\n// });\n\nimport React, { type ComponentType, type ReactNode } from 'react';\nimport { ImageStyle, TextStyle, ViewStyle, StyleProp, StyleSheet } from 'react-native';\n\n// Define a type that removes token strings from style properties\ntype ResolvedStyle = ViewStyle & TextStyle & ImageStyle;\n\n// StyleObject now extends ResolvedStyle and accepts type-safe token references\n// Generic parameter Tokens allows autocomplete and validation of token paths\n// Default to TokenConfig for backward compatibility\ntype StyleObject<Tokens extends TokenConfig = TokenConfig> = {\n [K in keyof ResolvedStyle]?:\n | ResolvedStyle[K]\n | (string extends ResolvedStyle[K]\n ? TokenPaths<Tokens>\n : number extends ResolvedStyle[K]\n ? TokenPaths<Tokens>\n : ResolvedStyle[K] extends string | number | undefined\n ? TokenPaths<Tokens>\n : never);\n};\n\n// Define the VariantOptions type to ensure type safety in variant definitions\ntype VariantOptions<V, Tokens extends TokenConfig = TokenConfig> = {\n [P in keyof V]: {\n [K in keyof V[P]]: StyleObject<Tokens>;\n };\n};\n\n// BooleanVariantKey type for 'true' and 'false' string literals\ntype BooleanVariantKey = 'true' | 'false';\n\ntype CompoundVariant<V, Tokens extends TokenConfig = TokenConfig> = {\n variants: Partial<{\n [P in keyof V]: keyof V[P] | boolean | Array<keyof V[P]>;\n }>;\n style: StyleObject<Tokens>;\n};\n\n// DefaultVariants type\ntype DefaultVariants<V> = Partial<{\n [P in keyof V]: keyof V[P] | boolean;\n}>;\n\n// VariantStyleConfig type\ntype VariantStyleConfig<\n V extends VariantOptions<V, Tokens>,\n Tokens extends TokenConfig = TokenConfig,\n> = {\n base?: StyleObject<Tokens>;\n variants: V;\n compoundVariants?: CompoundVariant<V, Tokens>[];\n defaultVariants?: DefaultVariants<V>;\n};\n\nexport type OmitUndefined<T> = T extends undefined ? never : T;\n\n/**\n * Helper type to make all properties optional if they have a default value\n */\ntype OptionalIfHasDefault<Props, Defaults> = Omit<Props, keyof Defaults> &\n Partial<Pick<Props, Extract<keyof Defaults, keyof Props>>>;\n\nexport type VariantProps<T> = T extends (props?: infer P) => StyleProp<ResolvedStyle> ? P : never;\n\n/**\n * Creates a function that generates styles based on variants\n */\nfunction styles<V extends VariantOptions<V, Tokens>, Tokens extends TokenConfig = TokenConfig>(\n config: VariantStyleConfig<V, Tokens>,\n) {\n type VariantProps = { [P in keyof V]: keyof V[P] | boolean | (string & {}) };\n type DefaultProps = NonNullable<typeof config.defaultVariants>;\n type Props = OptionalIfHasDefault<VariantProps, DefaultProps>;\n\n return (props?: Props): StyleProp<ResolvedStyle> => {\n // We'll build up styles in the correct hierarchy: base → variants → compound variants\n // Base styles (lowest priority)\n let stylesObj: StyleObject<Tokens> = {\n ...(config.base || {}),\n };\n\n // Merge default variants with provided props\n const mergedProps = {\n ...(config.defaultVariants || {}),\n ...props,\n } as VariantProps;\n\n // Apply variant styles (overrides base styles)\n for (const [propName, value] of Object.entries(mergedProps) as [\n keyof V,\n keyof VariantProps[keyof V] | boolean,\n ][]) {\n const variantGroup = config.variants[propName];\n if (variantGroup) {\n // Handle boolean variants\n if (typeof value === 'boolean') {\n const booleanValue: BooleanVariantKey = value ? 'true' : 'false';\n if (variantGroup[booleanValue as keyof typeof variantGroup]) {\n stylesObj = {\n ...stylesObj,\n ...variantGroup[booleanValue as keyof typeof variantGroup],\n };\n }\n } else {\n // Handle string/enum variants\n const variantValue = value || config.defaultVariants?.[propName];\n if (variantValue && variantGroup[variantValue as keyof typeof variantGroup]) {\n stylesObj = {\n ...stylesObj,\n ...variantGroup[variantValue as keyof typeof variantGroup],\n };\n }\n }\n }\n }\n\n // Apply compound variants (highest priority before inline styles)\n if (config.compoundVariants) {\n for (const compound of config.compoundVariants) {\n if (\n Object.entries(compound.variants).every(([propName, value]) => {\n // Handle boolean values in compound variants\n if (typeof value === 'boolean') {\n return mergedProps[propName as keyof V] === value;\n }\n\n // Handle array of values\n if (Array.isArray(value)) {\n return value.includes(mergedProps[propName as keyof V]);\n }\n\n // Handle single value (string/enum)\n return mergedProps[propName as keyof V] === value;\n })\n ) {\n stylesObj = {\n ...stylesObj,\n ...compound.style,\n };\n }\n }\n }\n\n // Create a StyleSheet for better performance\n return StyleSheet.create({\n style: stylesObj as ResolvedStyle,\n }).style;\n };\n}\n\n/**** Tokens definition */\n\n// Define allowed token categories and their value types\ninterface AllowedTokenCategories {\n colors: string;\n space: number;\n fontSize: number;\n fonts: string;\n lineHeight: number;\n}\n\n// Update TokenConfig to only allow specific categories\ntype TokenConfig = {\n [K in keyof AllowedTokenCategories]?: Record<string, AllowedTokenCategories[K]>;\n};\n\n// Helper to convert keys (including numeric) to string literals\ntype KeysToStrings<T> =\n T extends Record<string | number, any> ? `${keyof T & (string | number)}` : never;\n\n// Generate literal string types for all valid token paths from a config\n// Example: For config { colors: { white: '#fff', primary: '#000' }, space: { 1: 4, 2: 8 } }\n// Generates: \"$colors.white\" | \"$colors.primary\" | \"$space.1\" | \"$space.2\"\ntype TokenPaths<T extends TokenConfig> = {\n [Category in keyof T]: T[Category] extends Record<string | number, any>\n ? `$${Category & string}.${KeysToStrings<T[Category]>}`\n : never;\n}[keyof T];\n\n// Helper type to extract component props\ntype ComponentProps<T> = T extends ComponentType<infer P> ? P : never;\n\ntype StyledFunction<Tokens extends TokenConfig = TokenConfig> = <C extends ComponentType<any>>(\n Component: C,\n) => <P extends object = {}>(\n styleObject:\n | StyleObject<Tokens>\n | ((props: P & Omit<ComponentProps<C>, 'style'>) => StyleObject<Tokens>),\n) => ComponentType<\n P & Omit<ComponentProps<C>, 'style'> & { style?: StyleProp<ViewStyle | TextStyle | ImageStyle> }\n>;\n\ninterface CreateTokensReturn<Tokens extends TokenConfig = TokenConfig> {\n sva: <V extends VariantOptions<V, Tokens>>(\n config: VariantStyleConfig<V, Tokens>,\n ) => (props?: any) => StyleProp<ResolvedStyle>;\n tokens: Readonly<Tokens>;\n styled: StyledFunction<Tokens>;\n}\n\n// Helper to resolve token references in style objects\nfunction resolveTokens<Tokens extends TokenConfig>(\n style: StyleObject<Tokens>,\n tokens: Tokens,\n): StyleObject<Tokens> {\n return Object.entries(style).reduce<Record<string, ResolvedStyle[keyof ResolvedStyle]>>(\n (acc, [key, value]) => {\n if (typeof value !== 'string' || !value.startsWith('$')) {\n acc[key] = value;\n return acc;\n }\n\n const tokenPath = value.slice(1).split('.');\n const [category, token] = tokenPath;\n\n const tokenCategory = tokens[category as keyof Tokens] as\n | Record<string, string | number>\n | undefined;\n const tokenValue = tokenCategory?.[token];\n if (tokenValue !== undefined) {\n acc[key] = tokenValue;\n }\n\n return acc;\n },\n {},\n ) as StyleObject<Tokens>;\n}\n\n/**\n * Creates a token system and returns the styles function\n */\nexport function defineTokens<T extends TokenConfig>(tokenConfig: T): CreateTokensReturn<T> {\n // Create the tokens object\n const tokens = Object.entries(tokenConfig).reduce((acc, [category, values]) => {\n return {\n ...acc,\n [category]: values,\n };\n }, {} as T);\n\n // Create a wrapped styles function that resolves token references\n const sva = <V extends VariantOptions<V, T>>(config: VariantStyleConfig<V, T>) => {\n // Resolve tokens in base styles\n const resolvedBase = config.base ? resolveTokens(config.base, tokens) : config.base;\n\n // Resolve tokens in variants\n const resolvedVariants = config.variants\n ? (Object.entries(config.variants).reduce<Partial<V>>((acc, [key, variantGroup]) => {\n type VariantGroupType = Record<string, StyleObject<T>>;\n\n const resolvedGroup = Object.entries(variantGroup as VariantGroupType).reduce<\n Record<string, StyleObject<T>>\n >((groupAcc, [variantKey, variantStyles]) => {\n return {\n ...groupAcc,\n [variantKey]: resolveTokens(variantStyles, tokens),\n };\n }, {});\n\n return {\n ...acc,\n [key as keyof V]: resolvedGroup as V[keyof V],\n };\n }, {}) as V)\n : ({} as V);\n\n // Resolve tokens in compound variants\n const resolvedCompoundVariants = config.compoundVariants?.map((compound) => ({\n ...compound,\n style: resolveTokens(compound.style, tokens),\n }));\n\n // Return the styles function with resolved tokens\n return styles({\n ...config,\n base: resolvedBase,\n variants: resolvedVariants,\n compoundVariants: resolvedCompoundVariants,\n });\n };\n\n /**\n * Styled function for creating styled components with token-aware styles\n */\n const styled: StyledFunction<T> = <C extends ComponentType<any>>(Component: C) => {\n return <P extends object = {}>(\n styleObject:\n | StyleObject<T>\n | ((props: P & Omit<ComponentProps<C>, 'style'>) => StyleObject<T>),\n ) => {\n // Create and return a new component that applies the resolved styles\n const StyledComponent: ComponentType<\n P &\n Omit<ComponentProps<C>, 'style'> & {\n style?: StyleProp<ViewStyle | TextStyle | ImageStyle>;\n }\n > = (props) => {\n const {\n children,\n style: propStyle,\n ...rest\n } = props as {\n children?: ReactNode;\n style?: StyleProp<ViewStyle | TextStyle | ImageStyle>;\n };\n\n // If styleObject is a function, call it with props to get the style object\n const styleToResolve =\n typeof styleObject === 'function'\n ? styleObject(props as P & Omit<ComponentProps<C>, 'style'>)\n : styleObject;\n\n // Resolve tokens in the style object\n const resolvedStyle = resolveTokens(styleToResolve, tokens);\n\n // Create StyleSheet for the resolved style\n const styles = StyleSheet.create({\n style: resolvedStyle as ResolvedStyle,\n });\n\n // Styles hierarchy: base styles → variants → compound variants → inline styles\n // Inline styles (propStyle) must have highest priority, so they come last in the array\n const mergedStyle = propStyle\n ? [styles.style, ...(Array.isArray(propStyle) ? propStyle : [propStyle])]\n : styles.style;\n\n // We need to cast here to handle the component props correctly\n const componentProps = {\n ...rest,\n style: mergedStyle,\n } as ComponentProps<C>;\n\n // Use createElement instead of JSX to avoid syntax issues\n return React.createElement(Component, componentProps, children);\n };\n\n // Set display name for better debugging\n const componentName = Component.displayName || Component.name || 'Component';\n StyledComponent.displayName = `Styled(${componentName})`;\n\n return StyledComponent;\n };\n };\n\n return {\n sva,\n tokens,\n styled,\n };\n}\n"]}