npm package: @huaiyou/ui
Description: Shared React UI component library
Latest version: 1.0.0

# @huaiyou/ui

> 共享的 React UI 组件库，提供常用的基础组件。

## 📦 安装

```bash
pnpm add @huaiyou/ui react react-dom
```

## 🚀 快速开始

```tsx
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 按钮

可定制的按钮组件，支持多种样式和尺寸。

```tsx
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 卡片

内容容器组件，支持标题和额外操作。

```tsx
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 输入框

表单输入组件，支持标签、错误提示和辅助文本。

```tsx
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：

```javascript
// tailwind.config.js
module.exports = {
  content: ['./src/**/*.{js,ts,jsx,tsx}', './node_modules/@huaiyou/ui/dist/**/*.{js,mjs}'],
  theme: {
    extend: {},
  },
  plugins: [],
};
```

### 自定义样式

通过 `className` 属性添加自定义样式：

```tsx
<Button className="my-custom-button">
  Custom Button
</Button>

<Card className="shadow-2xl rounded-xl">
  Custom Card
</Card>
```

### 覆盖默认样式

```css
/* 全局样式文件 */
.my-custom-button {
  @apply bg-gradient-to-r from-purple-500 to-pink-500;
}
```

## 🔧 高级用法

### 表单示例

```tsx
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>
  );
}
```

### 组件组合

```tsx
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>
  );
}
```

## 📝 开发指南

### 添加新组件

1. 在 `src/` 目录创建组件文件
2. 导出组件类型和实现
3. 在 `src/index.ts` 中导出
4. 编写测试文件
5. 更新文档

```tsx
// 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>
  );
}
```

### 运行测试

```bash
# 运行测试
pnpm test

# 监听模式
pnpm test:watch

# 测试覆盖率
pnpm test:coverage

# UI 模式
pnpm test:ui
```

### 构建

```bash
# 构建组件库
pnpm build

# 开发模式（stub）
pnpm dev
```

## ✅ 测试

所有组件都有完整的测试覆盖，使用 Vitest + React Testing Library。

```tsx
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);
});
```

## 🤝 贡献

欢迎添加新组件或改进现有组件！请确保：

1. 遵循现有代码风格
2. 添加 TypeScript 类型
3. 编写测试
4. 更新文档

## 📄 License

MIT