docs: add comments to shared.ts

This commit is contained in:
p-sw 2024-05-19 14:30:11 +09:00
parent 56ae87cd81
commit 6ef88893bf

View File

@ -1,38 +1,91 @@
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
/**
* Takes a string, and returns boolean if it is "true" or "false".
* Otherwise, returns the string.
*
* @example
* ```
* type BooleanString = BooleanString<"true" | "false" | "other">
* // BooleanString = true | false | "other" = boolean | "other"
* ```
*/
type BooleanString<T extends string> = T extends "true" type BooleanString<T extends string> = T extends "true"
? true ? true
: T extends "false" : T extends "false"
? false ? false
: T; : T;
type RawVariantProps<V extends Record<string, Record<string, string>>> = { /**
[VariantKey in keyof V]?: BooleanString<keyof V[VariantKey] & string>; * A type that represents a variant object.
*
* @example
* ```
* const variant: VariantType = {
* opened: {
* true: "opacity-100",
* false: "opacity-0",
* }
* size: {
* sm: "small",
* md: "medium",
* lg: "large",
* },
* color: {
* red: "#ff0000",
* green: "#00ff00",
* blue: "#0000ff",
* },
* }
* ```
*/
type VariantType = Record<string, Record<string, string>>;
/**
* Takes VariantType, and returns a type that represents the variant object.
*
* @example
* ```
* type VariantKV = VariantKV<VariantType>
* // VariantKV = {
* // opened: BooleanString<"true" | "false"> = boolean;
* // size: BooleanString<"sm" | "md" | "lg">;
* // color: BooleanString<"red" | "green" | "blue">;
* // }
* ```
*/
type VariantKV<V extends VariantType> = {
[VariantKey in keyof V]: BooleanString<keyof V[VariantKey] & string>;
}; };
export function vcn<V extends Record<string, Record<string, string>>>({ export function vcn<V extends VariantType>({
base, base,
variants, variants,
defaults, defaults,
}: { }: {
base?: string | undefined; base?: string | undefined;
variants: V; variants: V /* VariantType */;
defaults: { defaults: VariantKV<V>;
[VariantKey in keyof V]: BooleanString<keyof V[VariantKey] & string>;
};
}): [ }): [
(variantProps: RawVariantProps<V> & { className?: string }) => string, (variantProps: Partial<VariantKV<V>> & { className?: string }) => string,
<AnyPropBeforeResolve extends Record<string, any>>( <AnyPropBeforeResolve extends Record<string, any>>(
anyProps: AnyPropBeforeResolve, anyProps: AnyPropBeforeResolve,
options?: { options?: {
excludeClassName?: boolean; excludeClassName?: boolean;
} }
) => [ ) => [
RawVariantProps<V> & { className?: string }, Partial<VariantKV<V>> & { className?: string },
Omit<AnyPropBeforeResolve, keyof RawVariantProps<V> | "className">, Omit<AnyPropBeforeResolve, keyof Partial<VariantKV<V>> | "className">,
], ],
] { ] {
return [ return [
/**
* Takes any props (including className), and returns the class name.
* If there is no variant specified in props, then it will fallback to default.
*
* @param variantProps - The variant props including className.
* @returns The class name.
*/
({ className, ...variantProps }) => { ({ className, ...variantProps }) => {
return twMerge( return twMerge(
base, base,
@ -41,13 +94,22 @@ export function vcn<V extends Record<string, Record<string, string>>>({
).map<string>( ).map<string>(
([variantKey, defaultValue]) => ([variantKey, defaultValue]) =>
variants[variantKey][ variants[variantKey][
(variantProps as unknown as RawVariantProps<V>)[variantKey] ?? (variantProps as unknown as Partial<VariantKV<V>>)[variantKey] ??
defaultValue defaultValue
] ]
), ),
className className
); );
}, },
/**
* Takes any props, parse variant props and other props.
* If `options.excludeClassName` is true, then it will parse className as "other" props.
* Otherwise, it will parse className as variant props.
*
* @param anyProps - Any props that have passed to the component.
* @param options - Options.
* @returns [variantProps, otherProps]
*/
(anyProps, options = {}) => { (anyProps, options = {}) => {
const variantKeys = Object.keys(variants) as (keyof V)[]; const variantKeys = Object.keys(variants) as (keyof V)[];
@ -63,13 +125,27 @@ export function vcn<V extends Record<string, Record<string, string>>>({
}, },
[{}, {}] [{}, {}]
) as [ ) as [
RawVariantProps<V> & { className?: string }, Partial<VariantKV<V>> & { className?: string },
Omit<typeof anyProps, keyof RawVariantProps<V> | "className">, Omit<typeof anyProps, keyof Partial<VariantKV<V>> | "className">,
]; ];
}, },
]; ];
} }
/**
* Extract the props type from return value of `vcn` function.
*
* @example
* ```
* const [variantProps, otherProps] = vcn({ ... })
* interface Props
* extends VariantProps<typeof variantProps>, OtherProps { ... }
*
* function Component(props: Props) {
* ...
* }
* ```
*/
export type VariantProps<F extends ReturnType<typeof vcn>[0]> = F extends ( export type VariantProps<F extends ReturnType<typeof vcn>[0]> = F extends (
props: infer P props: infer P
) => string ) => string