feat: add cva

This commit is contained in:
Mark Lawlor
2022-10-26 10:30:49 +10:00
parent bf72ed1fed
commit a2a01291c3
6 changed files with 99 additions and 42 deletions

25
package-lock.json generated
View File

@@ -14632,6 +14632,22 @@
"node": ">=0.10.0"
}
},
"node_modules/class-variance-authority": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.2.4.tgz",
"integrity": "sha512-JJKn9ESARiNEBBRdTSPB9/SwaPb+wi19DMX/r8BVSyp1dxHa3JyUxa2GQjEVag5rBi+O3pL64UZ0XhnQsMz+3w==",
"funding": {
"url": "https://joebell.co.uk"
},
"peerDependencies": {
"typescript": ">= 4.5.5 < 5"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/clean-css": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz",
@@ -34808,6 +34824,7 @@
"dependencies": {
"@babel/helper-module-imports": "7.18.6",
"@babel/types": "7.19.4",
"class-variance-authority": "^0.2.4",
"css-tree": "^2.2.1",
"find-cache-dir": "^3.3.2",
"micromatch": "^4.0.5",
@@ -45978,6 +45995,11 @@
}
}
},
"class-variance-authority": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.2.4.tgz",
"integrity": "sha512-JJKn9ESARiNEBBRdTSPB9/SwaPb+wi19DMX/r8BVSyp1dxHa3JyUxa2GQjEVag5rBi+O3pL64UZ0XhnQsMz+3w=="
},
"clean-css": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz",
@@ -54271,6 +54293,7 @@
"@types/react-native": "0.70.6",
"@types/use-sync-external-store": "0.0.3",
"babel-plugin-tester": "10.1.0",
"class-variance-authority": "*",
"css-tree": "^2.2.1",
"find-cache-dir": "^3.3.2",
"jest": "29.2.1",
@@ -60555,7 +60578,7 @@
"clsx": "1.2.1",
"dedent": "0.7.0",
"docusaurus-plugin-sass": "0.2.2",
"front-matter": "*",
"front-matter": "^4.0.2",
"lodash.debounce": "4.0.8",
"nativewind": "^3.0.0-65eddcf.0",
"prism-react-renderer": "1.3.3",

View File

@@ -50,8 +50,9 @@
"dependencies": {
"@babel/helper-module-imports": "7.18.6",
"@babel/types": "7.19.4",
"find-cache-dir": "^3.3.2",
"class-variance-authority": "^0.2.4",
"css-tree": "^2.2.1",
"find-cache-dir": "^3.3.2",
"micromatch": "^4.0.5",
"react-is": "^18.1.0",
"use-sync-external-store": "^1.1.0"

View File

@@ -0,0 +1,19 @@
import type {
StringToBoolean,
ClassValue,
ClassProp,
} from "class-variance-authority/dist/types";
type ConfigSchema = Record<string, Record<string, ClassValue>>;
type ConfigVariants<T extends ConfigSchema> = {
[Variant in keyof T]?: StringToBoolean<keyof T[Variant]> | null;
};
export type CVAConfig<T> = T extends ConfigSchema
? {
variants?: T;
defaultVariants?: ConfigVariants<T>;
compoundVariants?: (T extends ConfigSchema
? ConfigVariants<T> & ClassProp
: ClassProp)[];
}
: never;

View File

@@ -1,5 +1,6 @@
import type { ComponentType } from "react";
import type { Style } from "../transform-css/types";
import { CVAConfig } from "./cva";
export type PropsWithClassName<T> = T & {
className?: string;
@@ -8,11 +9,11 @@ export type PropsWithClassName<T> = T & {
baseTw?: string;
};
export interface StyledOptions<T, P extends keyof T> {
export type StyledOptions<T, P extends keyof T> = CVAConfig<T> & {
props?: Partial<Record<P, keyof Style | true>>;
classProps?: (keyof T & string)[];
baseClassName?: string;
}
};
/**
* Default

View File

@@ -25,6 +25,7 @@ import {
getStyleSet,
subscribeToStyleSheet,
} from "../../style-sheet/native/runtime";
import { cva } from "class-variance-authority";
const stateInheritanceContent = createContext<ConditionalStateRecord>({});
@@ -33,30 +34,42 @@ export function styled(
styledBaseClassNameOrOptions?:
| string
| StyledOptions<Record<string, unknown>, string>,
maybeOptions: StyledOptions<Record<string, unknown>, string> = {}
maybeOptions: StyledOptions<any, any> = {}
) {
const { props: propsToTransform, classProps } =
typeof styledBaseClassNameOrOptions === "object"
? styledBaseClassNameOrOptions
: maybeOptions;
const {
classProps,
baseClassName = "",
props: propsToTransform,
...cvaOptions
} = typeof styledBaseClassNameOrOptions === "object"
? styledBaseClassNameOrOptions
: maybeOptions;
const baseClassName =
const defaultClassName =
typeof styledBaseClassNameOrOptions === "string"
? styledBaseClassNameOrOptions
: maybeOptions?.baseClassName;
: baseClassName;
const Styled = forwardRef((props, ref) => {
return (
<StyledComponent
ref={ref}
component={component}
propsToTransform={propsToTransform}
classProps={classProps}
baseClassName={baseClassName}
{...props}
/>
);
});
const classGenerator = cva(defaultClassName, cvaOptions);
const Styled = forwardRef<unknown, any>(
({ className, tw, ...props }, ref) => {
const generatedClassName = classGenerator({
class: tw ?? className,
...props,
});
return (
<StyledComponent
ref={ref}
component={component}
propsToTransform={propsToTransform}
className={generatedClassName}
{...props}
/>
);
}
);
if (typeof component !== "string") {
Styled.displayName = `NativeWind.${
component.displayName || component.name || "NoName"
@@ -69,8 +82,7 @@ export function styled(
export const StyledComponent = forwardRef(function NativeWindStyledComponent(
{
component: Component,
baseClassName,
tw: twClassName,
tw,
className: propClassName,
propsToTransform,
classProps,
@@ -89,15 +101,11 @@ export const StyledComponent = forwardRef(function NativeWindStyledComponent(
*/
const [componentState, componentStateDispatch] = useComponentState();
const classNameWithDefaults = [baseClassName, twClassName ?? propClassName]
.filter(Boolean)
.join(" ");
/**
* Resolve the props/classProps/spreadProps options
*/
const { styledProps, className } = withStyledProps({
className: classNameWithDefaults,
className: tw ?? propClassName,
propsToTransform,
classProps,
componentState,

View File

@@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { ComponentType, ForwardedRef, forwardRef, useMemo } from "react";
import { StyleProp } from "react-native";
import { cva } from "class-variance-authority";
import { Style } from "../../transform-css/types";
import type { PropsWithClassName, StyledOptions } from "../index";
@@ -10,17 +11,19 @@ export function styled(
ref: ForwardedRef<unknown>;
}>,
styledBaseClassNameOrOptions?: string | StyledOptions<unknown, never>,
maybeOptions: StyledOptions<unknown, never> = {}
maybeOptions: StyledOptions<any, any> = {}
) {
const { classProps } =
const { classProps, baseClassName, props, ...cvaOptions } =
typeof styledBaseClassNameOrOptions === "object"
? styledBaseClassNameOrOptions
: maybeOptions;
const baseClassName =
const defaultClassName =
typeof styledBaseClassNameOrOptions === "string"
? styledBaseClassNameOrOptions
: maybeOptions?.baseClassName;
: baseClassName;
const classGenerator = cva(`${classProps} ${defaultClassName} `, cvaOptions);
return forwardRef(
(
@@ -32,17 +35,19 @@ export function styled(
}: PropsWithClassName<{ style: Style }>,
ref
) => {
let actualClassName = tw ?? className;
if (classProps) actualClassName = `${classProps} ${actualClassName}`;
if (baseClassName)
actualClassName = `${baseClassName} ${actualClassName}`;
const generatedClassName = classGenerator({
class: tw ?? className,
...props,
});
const style = useMemo(() => {
return actualClassName
? [{ $$css: true, tailwind: actualClassName } as Style, inlineStyle]
return generatedClassName
? [
{ $$css: true, tailwind: generatedClassName } as Style,
inlineStyle,
]
: inlineStyle;
}, [inlineStyle, actualClassName]);
}, [inlineStyle, generatedClassName]);
return <Component ref={ref} {...props} style={style} />;
}