This commit is contained in:
2026-05-13 02:49:13 +08:00
parent 33951a649b
commit 92e4fd11f1
18 changed files with 697 additions and 1717 deletions

View File

@@ -1,18 +0,0 @@
import { ComponentPropsWithRef, forwardRef, ReactNode } from "react";
export type ButtonState = {
loading?: boolean;
disabled?: boolean;
};
export type ButtonProps = {
size?: "xs" | "sm" | "md" | "lg";
shape?: "circle" | "rounded" | "square";
iconSvg?: ReactNode;
iconOnly?: boolean;
hideIcon?: boolean;
loadingIconSvg?: ReactNode;
} & ButtonState &
ComponentPropsWithRef<"button">;
export const Button = forwardRef();

View File

@@ -0,0 +1,9 @@
import { ComponentPropsWithRef } from "react";
type ButtonIconProps = ComponentPropsWithRef<"span">;
export const ButtonIcon = (props: ButtonIconProps) => {
const { children, ...rest } = props;
return <span {...rest}>{children}</span>;
};
ButtonIcon.displayName = "ButtonIcon";

View File

@@ -0,0 +1,9 @@
import { ComponentPropsWithRef } from "react";
type ButtonLoadingProps = ComponentPropsWithRef<"span">;
export const ButtonLoading = (props: ButtonLoadingProps) => {
const { children, ...rest } = props;
return <span {...rest}>{children}</span>;
};
ButtonLoading.displayName = "ButtonLoading";

View File

@@ -0,0 +1,38 @@
import { ComponentPropsWithoutRef, forwardRef } from "react";
import { useButtonSlot } from "./useButtonSlots";
export type ButtonRootOwnProps = {
iconOnly?: boolean;
hideIcon?: boolean;
};
export type ButtonRootStateProps = {
loading?: boolean;
disabled?: boolean;
};
export type ButtonRootPrimitiveProps = Omit<
ComponentPropsWithoutRef<"button">,
keyof ButtonRootStateProps
>;
export type ButtonRootProps = ButtonRootOwnProps &
ButtonRootStateProps &
ButtonRootPrimitiveProps;
export const ButtonRoot = forwardRef<HTMLButtonElement, ButtonRootProps>(
(props, ref) => {
const { children, loading, iconOnly, hideIcon, ...rest } = props;
const { iconNode, loadingNode } = useButtonSlot(children);
return (
<button ref={ref} {...rest}>
{loadingNode ?? loadingNode}
{!loadingNode && iconNode ? (hideIcon ? null : iconNode) : null}
{children}
</button>
);
},
);
ButtonRoot.displayName = "ButtonRoot";
// 我需要一个通用hook方法 useProps<ownProps,stateProps,primitiveProps>(props),返一个对象 { ownPropsstateProps,primitiveProps}

View File

@@ -0,0 +1,9 @@
import { ButtonIcon } from "./ButtonIcon";
import { ButtonLoading } from "./ButtonLoading";
import { ButtonRoot } from "./ButtonRoot";
export namespace Button {
export const Root = ButtonRoot;
export const Icon = ButtonIcon;
export const Loading = ButtonLoading;
}

View File

@@ -0,0 +1,29 @@
import React, { ReactNode, isValidElement, Children } from "react";
import { ButtonIcon } from "./ButtonIcon";
import { ButtonLoading } from "./ButtonLoading";
export function useButtonSlot(children: ReactNode) {
const iconNodes: React.ReactElement[] = [];
const loadingNodes: React.ReactElement[] = [];
const collectNodes = (nodes: ReactNode) => {
Children.forEach(nodes, (child) => {
if (!isValidElement(child)) return;
if (child.type === ButtonIcon) {
iconNodes.push(child);
} else if (child.type === ButtonLoading) {
loadingNodes.push(child);
} else if ((child as any).props.children) {
collectNodes((child as any).props.children);
}
});
};
collectNodes(children);
return {
iconNode: iconNodes.at(-1),
loadingNode: loadingNodes.at(-1),
};
}