@huaiyou/ui
v1.0.0
Published
Shared React UI component library
Downloads
55
Readme
@huaiyou/ui
共享的 React UI 组件库,提供常用的基础组件。
📦 安装
pnpm add @huaiyou/ui react react-dom🚀 快速开始
import { Button, Card, Input } from '@huaiyou/ui';
function App() {
return (
<Card title="Welcome">
<Input label="Email" placeholder="Enter your email" />
<Button variant="primary">Submit</Button>
</Card>
);
}📦 组件列表
Button 按钮
可定制的按钮组件,支持多种样式和尺寸。
import { Button } from '@huaiyou/ui';
// 基础用法
<Button>Default Button</Button>
// 不同样式
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="danger">Danger</Button>
// 不同尺寸
<Button size="small">Small</Button>
<Button size="medium">Medium</Button>
<Button size="large">Large</Button>
// 加载状态
<Button loading>Loading...</Button>
// 禁用状态
<Button disabled>Disabled</Button>Props:
| 属性 | 类型 | 默认值 | 说明 |
| -------- | -------------------------------------- | ----------- | -------- |
| variant | 'primary' \| 'secondary' \| 'danger' | 'primary' | 按钮样式 |
| size | 'small' \| 'medium' \| 'large' | 'medium' | 按钮尺寸 |
| loading | boolean | false | 加载状态 |
| disabled | boolean | false | 禁用状态 |
Card 卡片
内容容器组件,支持标题和额外操作。
import { Card, Button } from '@huaiyou/ui';
// 基础用法
<Card title="Card Title">
<p>Card content goes here</p>
</Card>
// 带额外操作
<Card
title="User Profile"
extra={<Button size="small">Edit</Button>}
>
<p>User information...</p>
</Card>
// 无标题
<Card>
<p>Simple card without title</p>
</Card>Props:
| 属性 | 类型 | 默认值 | 说明 |
| --------- | ----------- | ------ | -------------- |
| title | string | - | 卡片标题 |
| extra | ReactNode | - | 右上角额外操作 |
| children | ReactNode | - | 卡片内容 |
| className | string | - | 自定义类名 |
Input 输入框
表单输入组件,支持标签、错误提示和辅助文本。
import { Input } from '@huaiyou/ui';
import { useState } from 'react';
function Form() {
const [value, setValue] = useState('');
const [error, setError] = useState('');
return (
<>
{/* 基础用法 */}
<Input label="Username" value={value} onChange={(e) => setValue(e.target.value)} />
{/* 带错误提示 */}
<Input label="Email" type="email" error="Invalid email format" />
{/* 带辅助文本 */}
<Input label="Password" type="password" helperText="Must be at least 8 characters" />
{/* 占位符 */}
<Input label="Phone" placeholder="Enter your phone number" />
</>
);
}Props:
| 属性 | 类型 | 默认值 | 说明 |
| ---------- | -------- | ------ | ------------ |
| label | string | - | 输入框标签 |
| error | string | - | 错误提示信息 |
| helperText | string | - | 辅助文本 |
| className | string | - | 自定义类名 |
继承所有原生 <input> 属性。
🎨 样式定制
使用 Tailwind CSS
组件使用 Tailwind CSS 类名,确保你的项目已配置 Tailwind:
// tailwind.config.js
module.exports = {
content: ['./src/**/*.{js,ts,jsx,tsx}', './node_modules/@huaiyou/ui/dist/**/*.{js,mjs}'],
theme: {
extend: {},
},
plugins: [],
};自定义样式
通过 className 属性添加自定义样式:
<Button className="my-custom-button">
Custom Button
</Button>
<Card className="shadow-2xl rounded-xl">
Custom Card
</Card>覆盖默认样式
/* 全局样式文件 */
.my-custom-button {
@apply bg-gradient-to-r from-purple-500 to-pink-500;
}🔧 高级用法
表单示例
import { Button, Input, Card } from '@huaiyou/ui';
import { useState } from 'react';
function LoginForm() {
const [formData, setFormData] = useState({
email: '',
password: '',
});
const [errors, setErrors] = useState<Record<string, string>>({});
const [loading, setLoading] = useState(false);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
try {
// 验证
const newErrors: Record<string, string> = {};
if (!formData.email) {
newErrors.email = 'Email is required';
}
if (!formData.password) {
newErrors.password = 'Password is required';
}
if (Object.keys(newErrors).length > 0) {
setErrors(newErrors);
return;
}
// 提交表单
await login(formData);
} finally {
setLoading(false);
}
};
return (
<Card title="Login">
<form onSubmit={handleSubmit} className="space-y-4">
<Input
label="Email"
type="email"
value={formData.email}
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
error={errors.email}
/>
<Input
label="Password"
type="password"
value={formData.password}
onChange={(e) => setFormData({ ...formData, password: e.target.value })}
error={errors.password}
/>
<Button type="submit" loading={loading} className="w-full">
Login
</Button>
</form>
</Card>
);
}组件组合
import { Button, Card } from '@huaiyou/ui';
function Dashboard() {
return (
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<Card title="Total Users" extra={<Button size="small">View All</Button>}>
<div className="text-4xl font-bold">1,234</div>
<p className="text-gray-500">+12% from last month</p>
</Card>
<Card title="Revenue" extra={<Button size="small">Details</Button>}>
<div className="text-4xl font-bold">$12,345</div>
<p className="text-gray-500">+8% from last month</p>
</Card>
<Card title="Active Projects" extra={<Button size="small">Manage</Button>}>
<div className="text-4xl font-bold">42</div>
<p className="text-gray-500">5 completed this week</p>
</Card>
</div>
);
}📝 开发指南
添加新组件
- 在
src/目录创建组件文件 - 导出组件类型和实现
- 在
src/index.ts中导出 - 编写测试文件
- 更新文档
// src/NewComponent.tsx
import React from 'react';
export interface NewComponentProps {
title: string;
children?: React.ReactNode;
}
export function NewComponent({ title, children }: NewComponentProps) {
return (
<div>
<h2>{title}</h2>
{children}
</div>
);
}运行测试
# 运行测试
pnpm test
# 监听模式
pnpm test:watch
# 测试覆盖率
pnpm test:coverage
# UI 模式
pnpm test:ui构建
# 构建组件库
pnpm build
# 开发模式(stub)
pnpm dev✅ 测试
所有组件都有完整的测试覆盖,使用 Vitest + React Testing Library。
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Button } from './Button';
it('should handle click events', async () => {
const handleClick = vi.fn();
render(<Button onClick={handleClick}>Click me</Button>);
await userEvent.click(screen.getByText('Click me'));
expect(handleClick).toHaveBeenCalledTimes(1);
});🤝 贡献
欢迎添加新组件或改进现有组件!请确保:
- 遵循现有代码风格
- 添加 TypeScript 类型
- 编写测试
- 更新文档
📄 License
MIT
