import React, {
Children,
cloneElement,
FC,
isValidElement,
type HTMLElementType,
} from "react";
import { type CxInput, cx } from "../../utils/cx";
export interface StylesDescriptor {
base?: CxInput;
hover?: CxInput;
active?: CxInput;
focus?: CxInput;
disabled?: CxInput;
color?: CxInput;
size?: CxInput;
layer?: CxInput;
wrapper?: CxInput;
dark?: CxInput;
light?: CxInput;
sundry?: CxInput;
[key: string]: CxInput;
}
const isStylesDescriptor = (obj: any): obj is StylesDescriptor => {
return typeof obj === "object" && !!obj;
};
export type StylesType = StylesDescriptor | string;
export interface StylesProps {
/**
* @description_en either as a simple string or a categorized object with predefined or custom keys.
* @description 可以是简单字符串或包含预定义或自定义键的分类对象。
* @example
* ```tsx
* // Simple string
*
*
*
*
* // Categorized object
*
*
*
* ```
*/
className?: StylesType;
/**
* @description 传入容器标签名.是否生成包含所有 `className` 的 `wrapper`, 默认 false, 传递 `true` 为 `div。`
* @description_en Whether to generate a `wrapper` containing all `className`, default is false, and pass the container tag name, if `true` will be `div`.
*/
asWrapper?: boolean | HTMLElementType;
children?: React.ReactNode;
}
/**
* @description 分类编写样式和基本的string样式,内置类似 `clsx` 对类型描述对象的值进行组合,支持去除重复类名,支持嵌套。
* @description_en Categorized writing styles and basic string styles, built-in similar to `clsx` to combine the values of type description objects, support removing duplicate class names, and support nesting.
* @component
* @example
* ```tsx
*
*
*
* ```
*
* @example
* ```tsx
*
*
*
* ```
*/
export const Styles: FC = ({
className,
children,
asWrapper = false,
}) => {
if (!children) {
return null;
}
if (!className) {
return children;
}
const generatedClassName =
typeof className === "string" ? className : cx(...Object.values(className));
if (asWrapper) {
const Tag = asWrapper === true ? "div" : asWrapper;
return {children};
}
if (Children.count(children) > 1) {
console.error(
": children has more than one child. Please check your code."
);
return children;
}
if (isValidElement(children)) {
const typeChildren = children as any;
let processedChildClassName = typeChildren?.props?.className;
if (typeChildren?.type?.displayName === Styles.displayName) {
if (isStylesDescriptor(processedChildClassName)) {
processedChildClassName = cx(...Object.values(processedChildClassName));
}
}
return cloneElement(children, {
className: cx(generatedClassName, processedChildClassName),
} as any);
}
console.error(
": children is not a valid React element. Please check your code."
);
return children;
};
Styles.displayName = "W/Styles";