npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@gulibs/vgrove-ui

v0.0.187

Published

VGrove UI component library built with HeroUI and React

Readme

VGrove UI

现代化企业级 React UI 组件库 - 基于 HeroUI 构建,集成完整的认证、表单、布局和国际化解决方案

npm version License: MIT TypeScript

✨ 核心特性

🔐 现代化认证系统

  • 标准化 Loader API: 基于 React Router v7+ loader/action 模式
  • 智能路由保护: 多层级认证、角色和权限检查
  • 自动重定向: 智能跳转到登录页面或权限不足页面
  • 中间件支持: 可扩展的认证中间件链

📝 完整表单解决方案

  • 基于 React Hook Form: 高性能表单处理
  • 智能表单状态: watchNames 自动控制按钮启用状态
  • 丰富的表单组件: 15+ 个企业级表单组件
  • 统一验证系统: 实时验证和错误处理

🎨 企业级 UI 组件

  • 基于 HeroUI: 现代化设计语言
  • 完整布局系统: 响应式布局和页面管理
  • 灵活主题系统: 明暗主题 + 系统主题检测
  • 国际化支持: 内置多语言和本地化

⚡ 性能与开发体验

  • 智能缓存: 路由级别的性能优化
  • 完整 TypeScript: 类型安全和智能提示
  • 调试工具: 模块化调试配置
  • 零配置: 开箱即用的企业级功能

📦 安装

# npm
npm install @gulibs/vgrove-ui

# pnpm (推荐)
pnpm add @gulibs/vgrove-ui

# yarn
yarn add @gulibs/vgrove-ui

对等依赖

VGrove UI 需要以下对等依赖:

# 核心依赖
npm install react react-dom react-router-dom

# 样式依赖
npm install @heroui/react @heroui/theme framer-motion
npm install tailwindcss @tailwindcss/vite

# 表单依赖
npm install react-hook-form

# 可选依赖
npm install react-helmet-async  # 用于页面 SEO 管理

Tailwind CSS 配置

// tailwind.config.js
const { heroui } = require("@heroui/react");

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
    "./node_modules/@heroui/theme/dist/**/*.{js,ts,jsx,tsx}",
    "./node_modules/@gulibs/vgrove-ui/dist/**/*.{js,ts,jsx,tsx}"
  ],
  theme: {
    extend: {},
  },
  darkMode: "class",
  plugins: [heroui()]
}

🚀 快速开始

基础设置

import React from 'react';
import { VGroveProvider, VGroveLayout } from '@gulibs/vgrove-ui';

// 侧边栏菜单配置
const menuItems = [
  {
    key: 'dashboard',
    label: '仪表板',
    href: '/dashboard',
    icon: <DashboardIcon />
  },
  {
    key: 'users',
    label: '用户管理',
    href: '/users',
    icon: <UsersIcon />
  }
];

function App() {
  return (
    <VGroveProvider
      settings={{
        sidebar: {
          collapsed: false,
          collapsible: true
        },
        navbar: {
          height: 64
        }
      }}
    >
      <VGroveLayout
        items={menuItems}
        onCollapsedChange={(collapsed) => console.log('Sidebar collapsed:', collapsed)}
      >
        {/* 你的应用路由 */}
        <Router />
      </VGroveLayout>
    </VGroveProvider>
  );
}

export default App;

认证系统快速配置

import {
  quickSetup,
  createBrowserRouter,
  RouterProvider
} from '@gulibs/vgrove-ui';

// 快速配置认证系统
const { authLoader, loginAction, logoutAction } = quickSetup({
  // 认证检查函数
  authCheck: async () => {
    const token = localStorage.getItem('authToken');
    if (!token) return null;

    const user = await fetchUserProfile(token);
    return user;
  },

  // 登录 API
  loginApi: async (credentials) => {
    const response = await fetch('/api/auth/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(credentials)
    });

    if (!response.ok) throw new Error('登录失败');

    const { user, token } = await response.json();
    localStorage.setItem('authToken', token);

    return { user, token };
  },

  // 配置选项
  loginPath: '/auth/login',
  forbiddenPath: '/403',
  publicPaths: ['/auth/*', '/public/*', '/'],
  debug: process.env.NODE_ENV === 'development'
});

// 创建路由
const router = createBrowserRouter([
  // 公共路由
  {
    path: '/auth/login',
    element: <LoginPage />,
    action: loginAction
  },

  // 需要认证的路由
  {
    path: '/dashboard',
    element: <Dashboard />,
    loader: authLoader
  },

  // 需要管理员角色的路由
  {
    path: '/admin',
    element: <AdminPanel />,
    loader: createRoleLoader(['admin', 'super-admin'])
  },

  // 需要特定权限的路由
  {
    path: '/users',
    element: <UserManagement />,
    loader: createPermissionLoader(['user.read', 'user.write'])
  }
]);

function App() {
  return (
    <VGroveProvider>
      <RouterProvider router={router} />
    </VGroveProvider>
  );
}

🔐 认证与路由系统

VGrove UI 提供了基于 React Router v7+ 的现代化认证和权限管理系统,支持多层级认证检查和智能路由保护。

核心 Loader API

基础认证 Loader

import { createAuthLoader, configureLoaders } from '@gulibs/vgrove-ui';

// 全局配置 (一次性配置)
configureLoaders({
  authCheck: async () => {
    const token = localStorage.getItem('token');
    if (!token) return null;
    return await fetchUserProfile(token);
  },
  loginPath: '/auth/login',
  forbiddenPath: '/403',
  publicPaths: ['/auth/*', '/public/*', '/404', '/403']
});

// 创建认证 Loader
const authLoader = createAuthLoader();

// 在路由中使用
{
  path: '/dashboard',
  element: <Dashboard />,
  loader: authLoader  // 需要用户登录
}

角色检查 Loader

import { createRoleLoader } from '@gulibs/vgrove-ui';

// 检查用户是否具有指定角色
const adminLoader = createRoleLoader(['admin', 'super-admin']);
const moderatorLoader = createRoleLoader(['moderator']);

// 使用示例
{
  path: '/admin',
  element: <AdminPanel />,
  loader: adminLoader  // 只有 admin 或 super-admin 可以访问
}

权限检查 Loader

import { createPermissionLoader } from '@gulibs/vgrove-ui';

// 检查用户是否具有指定权限
const userManagementLoader = createPermissionLoader([
  'user.read',
  'user.write'
]);

// 使用示例
{
  path: '/users',
  element: <UserManagement />,
  loader: userManagementLoader  // 需要用户读写权限
}

核心 Action API

登录 Action

import { createLoginAction } from '@gulibs/vgrove-ui';

const loginAction = createLoginAction(async (credentials) => {
  const response = await fetch('/api/auth/login', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(credentials)
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(error.message || '登录失败');
  }

  const { user, token } = await response.json();
  localStorage.setItem('authToken', token);

  return { user, token };
});

// 在路由中使用
{
  path: '/auth/login',
  element: <LoginPage />,
  action: loginAction
}

自定义 Action

import { createAction } from '@gulibs/vgrove-ui';

const updateProfileAction = createAction({
  // 表单验证
  validator: async (formData) => {
    const errors = [];
    if (!formData.get('name')) {
      errors.push({ field: 'name', message: '姓名不能为空' });
    }
    return errors;
  },

  // 数据处理
  processor: async (formData, user) => {
    return await updateUserProfile(user.id, {
      name: formData.get('name'),
      email: formData.get('email')
    });
  },

  requireAuth: true,
  successRedirect: '/profile'
});

📝 表单组件系统

VGrove UI 提供了基于 React Hook Form 的完整表单解决方案,包含 15+ 个企业级表单组件和智能表单状态管理。

核心表单组件

Form 表单容器

Form 组件是表单系统的核心,提供了统一的表单处理、验证和状态管理。

import { Form, FormItem, Input, Button } from '@gulibs/vgrove-ui';

function UserForm() {
  const handleSubmit = async (data) => {
    console.log('表单数据:', data);
    // 处理表单提交
  };

  const handleError = (errors) => {
    console.log('验证错误:', errors);
  };

  return (
    <Form
      mode="onChange"  // 验证模式: onChange | onBlur | onSubmit | all
      watchNames={['username', 'email']}  // 监听字段,控制按钮状态
      onFinish={handleSubmit}
      onFinishError={handleError}
      footer={{
        submitText: '提交',
        resetText: '重置',
        hiddenReset: false
      }}
    >
      <FormItem name="username" label="用户名" required>
        <Input placeholder="请输入用户名" />
      </FormItem>

      <FormItem name="email" label="邮箱" required>
        <Input type="email" placeholder="请输入邮箱" />
      </FormItem>
    </Form>
  );
}

Form 组件特性:

  • 智能按钮状态: watchNames 属性自动控制提交按钮启用 / 禁用
  • 多种验证模式: 支持实时验证和提交时验证
  • 可配置页脚: 自定义提交和重置按钮
  • 完整 TypeScript 支持: 类型安全的表单处理

FormItem 表单项包装器

FormItem 为表单字段提供统一的标签、验证和错误显示。

import { FormItem, Input, Textarea, Select } from '@gulibs/vgrove-ui';

function ProfileForm() {
  return (
    <Form onFinish={handleSubmit}>
      {/* 基础表单项 */}
      <FormItem
        name="name"
        label="姓名"
        required
        help="请输入您的真实姓名"
        rules={{
          required: { value: true, message: '姓名必填' },
          minLength: { value: 2, message: '姓名至少2个字符' }
        }}
      >
        <Input />
      </FormItem>

      {/* 带工具提示的表单项 */}
      <FormItem
        name="bio"
        label="个人简介"
        tooltip={{
          content: "简要介绍您的背景和经历",
          placement: "top"
        }}
      >
        <Textarea rows={4} />
      </FormItem>

      {/* 自定义验证状态 */}
      <FormItem
        name="status"
        label="状态"
        validateStatus="success"
      >
        <Select>
          <option value="active">活跃</option>
          <option value="inactive">非活跃</option>
        </Select>
      </FormItem>
    </Form>
  );
}

FormItem 特性:

  • 灵活布局: 支持垂直和水平布局
  • 工具提示: 内置 Tooltip 支持
  • 验证状态: 视觉化验证反馈
  • 帮助文本: 支持帮助信息显示

FormGroup 表单分组

用于将相关的表单字段分组显示。

import { FormGroup, FormItem, Input } from '@gulibs/vgrove-ui';

function AddressForm() {
  return (
    <Form onFinish={handleSubmit}>
      <FormGroup title="基本信息">
        <FormItem name="firstName" label="名">
          <Input />
        </FormItem>
        <FormItem name="lastName" label="姓">
          <Input />
        </FormItem>
      </FormGroup>

      <FormGroup title="联系信息">
        <FormItem name="email" label="邮箱">
          <Input type="email" />
        </FormItem>
        <FormItem name="phone" label="电话">
          <Input type="tel" />
        </FormItem>
      </FormGroup>
    </Form>
  );
}

输入组件

Input 输入框

基于 HeroUI Input 的增强版本,集成了表单验证和状态管理。

import { Form, FormItem, Input } from '@gulibs/vgrove-ui';

function InputExamples() {
  return (
    <Form onFinish={handleSubmit}>
      {/* 基础输入框 */}
      <FormItem name="username" label="用户名" required>
        <Input
          placeholder="请输入用户名"
          startContent={<UserIcon />}
        />
      </FormItem>

      {/* 密码输入框 */}
      <FormItem name="password" label="密码" required>
        <Input
          type="password"
          placeholder="请输入密码"
          endContent={<EyeIcon />}
        />
      </FormItem>

      {/* 带验证规则的输入框 */}
      <FormItem
        name="email"
        label="邮箱"
        required
        rules={{
          pattern: {
            value: /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/,
            message: '请输入有效的邮箱地址'
          }
        }}
      >
        <Input
          type="email"
          placeholder="[email protected]"
        />
      </FormItem>

      {/* 数字输入框 */}
      <FormItem name="age" label="年龄">
        <Input
          type="number"
          min={1}
          max={120}
          placeholder="请输入年龄"
        />
      </FormItem>
    </Form>
  );
}

Textarea 多行文本

import { FormItem, Textarea } from '@gulibs/vgrove-ui';

<FormItem
  name="description"
  label="描述"
  rules={{
    maxLength: { value: 500, message: '描述不能超过500字符' }
  }}
>
  <Textarea
    rows={4}
    placeholder="请输入详细描述..."
    maxLength={500}
  />
</FormItem>

Autocomplete 自动完成

import { FormItem, Autocomplete } from '@gulibs/vgrove-ui';

const suggestions = [
  { value: 'apple', label: '苹果' },
  { value: 'banana', label: '香蕉' },
  { value: 'orange', label: '橙子' }
];

<FormItem name="fruit" label="选择水果">
  <Autocomplete
    options={suggestions}
    placeholder="输入或选择水果"
    allowsCustomValue
  />
</FormItem>

选择组件

Select 下拉选择

import { FormItem, Select } from '@gulibs/vgrove-ui';

<FormItem name="category" label="分类" required>
  <Select placeholder="请选择分类">
    <option value="tech">技术</option>
    <option value="design">设计</option>
    <option value="business">商业</option>
  </Select>
</FormItem>

Radio 单选按钮

import { FormItem, Radio, RadioGroup } from '@gulibs/vgrove-ui';

// 单个单选按钮
<FormItem name="agree" label="同意条款">
  <Radio value="yes">我同意用户协议</Radio>
</FormItem>

// 单选按钮组
<FormItem name="gender" label="性别">
  <RadioGroup orientation="horizontal">
    <Radio value="male">男</Radio>
    <Radio value="female">女</Radio>
    <Radio value="other">其他</Radio>
  </RadioGroup>
</FormItem>

Checkbox 复选框

import { FormItem, Checkbox, CheckboxGroup } from '@gulibs/vgrove-ui';

// 单个复选框
<FormItem name="newsletter" label="订阅">
  <Checkbox>订阅邮件通知</Checkbox>
</FormItem>

// 复选框组
<FormItem name="interests" label="兴趣爱好">
  <CheckboxGroup>
    <Checkbox value="reading">阅读</Checkbox>
    <Checkbox value="travel">旅行</Checkbox>
    <Checkbox value="music">音乐</Checkbox>
    <Checkbox value="sports">运动</Checkbox>
  </CheckboxGroup>
</FormItem>

特殊输入组件

Switch 开关

import { FormItem, Switch } from '@gulibs/vgrove-ui';

<FormItem name="notifications" label="推送通知">
  <Switch
    defaultSelected
    color="success"
    startContent={<BellIcon />}
    endContent={<BellSlashIcon />}
  >
    启用推送通知
  </Switch>
</FormItem>

Slider 滑动条

import { FormItem, Slider } from '@gulibs/vgrove-ui';

<FormItem name="volume" label="音量">
  <Slider
    step={10}
    minValue={0}
    maxValue={100}
    defaultValue={50}
    className="max-w-md"
  />
</FormItem>

DatePicker 日期选择器

import { FormItem, DatePicker } from '@gulibs/vgrove-ui';

<FormItem name="birthdate" label="出生日期">
  <DatePicker
    label="选择日期"
    className="max-w-sm"
  />
</FormItem>

<FormItem name="appointment" label="预约时间">
  <DatePicker
    label="选择日期时间"
    showTimeSelect
    timeFormat="HH:mm"
    timeIntervals={15}
    dateFormat="MMMM d, yyyy h:mm aa"
  />
</FormItem>

TimeInput 时间输入

import { FormItem, TimeInput } from '@gulibs/vgrove-ui';

<FormItem name="startTime" label="开始时间">
  <TimeInput
    label="时间"
    hourCycle={24}
  />
</FormItem>

表单验证

内置验证规则

import { Form, FormItem, Input } from '@gulibs/vgrove-ui';

function ValidationExample() {
  return (
    <Form onFinish={handleSubmit}>
      <FormItem
        name="username"
        label="用户名"
        required
        rules={{
          required: { value: true, message: '用户名必填' },
          minLength: { value: 3, message: '用户名至少3个字符' },
          maxLength: { value: 20, message: '用户名最多20个字符' },
          pattern: {
            value: /^[a-zA-Z0-9_]+$/,
            message: '用户名只能包含字母、数字和下划线'
          }
        }}
      >
        <Input placeholder="请输入用户名" />
      </FormItem>

      <FormItem
        name="password"
        label="密码"
        required
        rules={{
          required: { value: true, message: '密码必填' },
          minLength: { value: 6, message: '密码至少6个字符' },
          validate: {
            hasNumber: (value) => /\\d/.test(value) || '密码必须包含数字',
            hasLetter: (value) => /[a-zA-Z]/.test(value) || '密码必须包含字母'
          }
        }}
      >
        <Input type="password" placeholder="请输入密码" />
      </FormItem>
    </Form>
  );
}

自定义验证

// 异步验证
const checkUsernameAvailability = async (username) => {
  const response = await fetch(`/api/check-username/${username}`);
  const { available } = await response.json();
  return available || '用户名已被占用';
};

<FormItem
  name="username"
  label="用户名"
  rules={{
    validate: {
      availability: checkUsernameAvailability
    }
  }}
>
  <Input placeholder="请输入用户名" />
</FormItem>

FormInstance API

VGrove UI 提供了增强的 FormInstance 接口,它扩展了 react-hook-form 的 UseFormReturn,并添加了更直观的 API 方法。

接口定义

interface FormInstance<TFieldValues> extends UseFormReturn<TFieldValues> {
  // 基础方法
  getFieldValue: (name: FieldPath<TFieldValues>) => any;
  getFieldsValue: () => TFieldValues;
  setFieldValue: (name: FieldPath<TFieldValues>, value: any) => void;
  setFieldsValue: (values: Partial<TFieldValues>) => void;
  resetFields: () => void;

  // 验证方法
  validateFields: () => Promise<boolean>;
  setFieldError: (name: FieldPath<TFieldValues>, error: any) => void;
  clearFieldErrors: (names?: FieldPath<TFieldValues>[]) => void;
}

useForm Hook 使用

import { useForm } from '@gulibs/vgrove-ui';

function UserForm() {
  // useForm 现在返回 FormInstance 而非 UseFormReturn
  const form = useForm<{ username: string; email: string }>();

  const handleTestAPI = () => {
    // 新增的便捷方法
    const username = form.getFieldValue('username');
    const allValues = form.getFieldsValue();

    form.setFieldValue('username', 'newValue');
    form.setFieldsValue({ username: 'test', email: '[email protected]' });

    form.resetFields();

    form.validateFields().then(isValid => {
      console.log('表单验证结果:', isValid);
    });

    form.setFieldError('username', { type: 'manual', message: '用户名已存在' });
    form.clearFieldErrors(['username']);

    // 仍然可以使用所有原生 react-hook-form 方法
    console.log('FormState:', form.formState);
    console.log('Watch:', form.watch());
  };

  return (
    <Form form={form} onFinish={console.log}>
      <FormItem name="username" label="用户名">
        <Input />
      </FormItem>
      <FormItem name="email" label="邮箱">
        <Input type="email" />
      </FormItem>
      <Button onClick={handleTestAPI}>测试 API</Button>
    </Form>
  );
}

createFormInstance 工具函数

如果您已经有一个 UseFormReturn 实例,可以使用 createFormInstance 将其转换为 FormInstance:

import { useForm as useRHFForm } from 'react-hook-form';
import { createFormInstance } from '@gulibs/vgrove-ui';

function MyComponent() {
  // 使用原生 react-hook-form
  const rhfForm = useRHFForm();

  // 转换为 FormInstance
  const form = createFormInstance(rhfForm);

  // 现在可以使用增强的 API
  const handleSubmit = () => {
    const values = form.getFieldsValue();
    console.log('表单值:', values);
  };

  return (
    <Form form={form} onFinish={handleSubmit}>
      {/* 表单内容 */}
    </Form>
  );
}

API 方法详解

| 方法 | 说明 | 对应的 react-hook-form 方法 | |------|------|----------------------------| | getFieldValue(name) | 获取单个字段值 | getValues(name) | | getFieldsValue() | 获取所有字段值 | getValues() | | setFieldValue(name, value) | 设置单个字段值 | setValue(name, value) | | setFieldsValue(values) | 批量设置字段值 | 多次调用 setValue | | resetFields() | 重置表单 | reset() | | validateFields() | 验证表单并返回结果 | trigger() | | setFieldError(name, error) | 设置字段错误 | setError(name, error) | | clearFieldErrors(names?) | 清除字段错误 | clearErrors(names) |

与原生 react-hook-form 的兼容性

FormInstance 完全兼容 react-hook-form 的 UseFormReturn 接口,您可以无缝使用所有原生方法:

function CompatibilityExample() {
  const form = useForm();

  const testCompatibility = () => {
    // VGrove UI 增强方法
    form.setFieldsValue({ name: 'test' });

    // 原生 react-hook-form 方法
    form.register('username');
    form.handleSubmit(console.log);
    form.formState.errors;
    form.control;

    // 两者可以混合使用
    const isValid = await form.validateFields(); // VGrove UI 方法
    if (!isValid) {
      form.setFocus('username'); // 原生方法
    }
  };

  return <Button onClick={testCompatibility}>测试兼容性</Button>;
}

表单 Hooks

useWatch 监听字段变化

import { useWatch, useFieldValue, useFieldsWatcher } from '@gulibs/vgrove-ui';

function FormWithHooks() {
  // 监听单个字段
  const username = useFieldValue('username');

  // 监听多个字段
  const [firstName, lastName] = useWatch(['firstName', 'lastName']);

  // 监听字段并获取状态
  const { values, allFilled, anyFilled } = useFieldsWatcher(['name', 'email']);

  return (
    <Form>
      <FormItem name="username" label="用户名">
        <Input />
      </FormItem>

      <FormItem name="firstName" label="名">
        <Input />
      </FormItem>

      <FormItem name="lastName" label="姓">
        <Input />
      </FormItem>

      {/* 实时显示 */}
      <div className="p-4 bg-gray-50 rounded">
        <p>用户名: {username}</p>
        <p>全名: {firstName && lastName ? `${firstName} ${lastName}` : '未完整'}</p>
        <p>表单状态: {allFilled ? '完成' : anyFilled ? '部分完成' : '未开始'}</p>
      </div>
    </Form>
  );
}

useFormContext 获取表单上下文

useFormContext 现在返回 FormInstance 而非 UseFormReturn,提供了更便捷的 API:

import { useFormContext } from '@gulibs/vgrove-ui';

function CustomFormComponent() {
  // 获取 FormInstance,包含所有增强方法
  const form = useFormContext();

  const handleReset = () => {
    // 使用新的便捷方法
    form.setFieldsValue({ username: '', email: '' });
    // 或者使用原生方法
    form.setValue('username', '');
    form.setValue('email', '');
  };

  const handleQuickFill = () => {
    // 批量设置字段值
    form.setFieldsValue({
      username: 'testuser',
      email: '[email protected]'
    });
  };

  const handleValidate = async () => {
    // 使用增强的验证方法
    const isValid = await form.validateFields();
    if (!isValid) {
      console.log('表单验证失败');
    }
  };

  return (
    <div className="space-y-4">
      <div className="flex space-x-2">
        <Button onClick={handleReset} disabled={form.formState.isSubmitting}>
          重置表单
        </Button>

        <Button onClick={handleQuickFill} variant="flat">
          快速填充
        </Button>

        <Button onClick={handleValidate} variant="flat">
          验证表单
        </Button>
      </div>

      {Object.keys(form.formState.errors).length > 0 && (
        <div className="text-red-500">
          表单有 {Object.keys(form.formState.errors).length} 个错误
        </div>
      )}

      {/* 显示当前表单值 */}
      <div className="p-4 bg-gray-50 rounded">
        <h4 className="font-semibold mb-2">当前表单值:</h4>
        <pre className="text-sm">
          {JSON.stringify(form.getFieldsValue(), null, 2)}
        </pre>
      </div>
    </div>
  );
}

在表单外部使用 FormInstance

您可以通过全局注册的方式在表单外部访问 FormInstance:

import { useForm, registerFormInstance, getFormInstance } from '@gulibs/vgrove-ui';

function FormComponent() {
  const form = useForm({
    registerAsGlobal: true,
    globalKey: 'userForm' // 可选,不提供则作为默认实例
  });

  return (
    <Form form={form}>
      <FormItem name="username" label="用户名">
        <Input />
      </FormItem>
    </Form>
  );
}

function ExternalController() {
  const handleFillForm = () => {
    // 获取全局注册的表单实例
    const form = getFormInstance('userForm');
    if (form) {
      form.setFieldValue('username', '外部设置的值');
    }
  };

  const handleValidateForm = async () => {
    const form = getFormInstance('userForm');
    if (form) {
      const isValid = await form.validateFields();
      console.log('外部验证结果:', isValid);
    }
  };

  return (
    <div className="space-x-2">
      <Button onClick={handleFillForm}>填充表单</Button>
      <Button onClick={handleValidateForm}>验证表单</Button>
    </div>
  );
}

完整表单示例

以下示例展示了如何使用新的 FormInstance API 构建完整的表单:

import React from 'react';
import {
  Form,
  FormItem,
  FormGroup,
  Input,
  Select,
  Textarea,
  Checkbox,
  Switch,
  DatePicker,
  useForm,
  Button
} from '@gulibs/vgrove-ui';

interface UserFormData {
  firstName: string;
  lastName: string;
  email: string;
  birthdate?: string;
  position: string;
  bio?: string;
  newsletter: boolean;
  notifications: boolean;
}

function CompleteUserForm() {
  // 使用增强的 useForm,返回 FormInstance
  const form = useForm<UserFormData>({
    mode: 'onChange',
    defaultValues: {
      newsletter: false,
      notifications: true
    }
  });

  const handleSubmit = async (data: UserFormData) => {
    try {
      console.log('提交数据:', data);

      // 使用 FormInstance 方法验证
      const isValid = await form.validateFields();
      if (!isValid) {
        console.log('表单验证失败');
        return;
      }

      // 调用 API 保存数据
      await saveUserProfile(data);

      // 成功后可以重置表单
      form.resetFields();
    } catch (error) {
      console.error('保存失败:', error);
      // 设置错误信息
      form.setFieldError('email', {
        type: 'manual',
        message: '保存失败,请检查邮箱是否已被使用'
      });
    }
  };

  const handleError = (errors: any) => {
    console.log('验证错误:', errors);
  };

  // 快速填充示例数据
  const handleQuickFill = () => {
    form.setFieldsValue({
      firstName: '张',
      lastName: '三',
      email: '[email protected]',
      position: 'developer',
      bio: '这是一个测试用户的个人简介。'
    });
  };

  // 获取当前表单值
  const handleGetValues = () => {
    const allValues = form.getFieldsValue();
    const firstName = form.getFieldValue('firstName');
    console.log('所有值:', allValues);
    console.log('名字:', firstName);
  };

  return (
    <div className="max-w-2xl mx-auto p-6 space-y-6">
      {/* 表单操作按钮 */}
      <div className="flex space-x-2 mb-4">
        <Button onClick={handleQuickFill} variant="flat">
          快速填充
        </Button>
        <Button onClick={handleGetValues} variant="flat">
          获取表单值
        </Button>
        <Button
          onClick={() => form.resetFields()}
          variant="flat"
          color="warning"
        >
          重置表单
        </Button>
      </div>

      <Form
        form={form}
        watchNames={['firstName', 'lastName', 'email']}
        onFinish={handleSubmit}
        onFinishError={handleError}
        footer={{
          submitText: '保存用户信息',
          resetText: '重置表单'
        }}
      >
      <FormGroup title="基本信息">
        <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
          <FormItem name="firstName" label="名" required>
            <Input placeholder="请输入名" />
          </FormItem>

          <FormItem name="lastName" label="姓" required>
            <Input placeholder="请输入姓" />
          </FormItem>
        </div>

        <FormItem
          name="email"
          label="邮箱"
          required
          rules={{
            pattern: {
              value: /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/,
              message: '请输入有效的邮箱地址'
            }
          }}
        >
          <Input type="email" placeholder="[email protected]" />
        </FormItem>

        <FormItem name="birthdate" label="出生日期">
          <DatePicker className="max-w-sm" />
        </FormItem>
      </FormGroup>

      <FormGroup title="工作信息">
        <FormItem name="position" label="职位">
          <Select placeholder="请选择职位">
            <option value="developer">开发工程师</option>
            <option value="designer">设计师</option>
            <option value="pm">产品经理</option>
            <option value="other">其他</option>
          </Select>
        </FormItem>

        <FormItem name="bio" label="个人简介">
          <Textarea
            rows={4}
            placeholder="请简要介绍您的背景和经历..."
            maxLength={500}
          />
        </FormItem>
      </FormGroup>

      <FormGroup title="设置">
        <FormItem name="newsletter" label="邮件订阅">
          <Checkbox>订阅产品更新和新闻</Checkbox>
        </FormItem>

        <FormItem name="notifications" label="推送通知">
          <Switch defaultSelected>
            启用桌面通知
          </Switch>
        </FormItem>
      </FormGroup>
    </Form>
  );
}

export default CompleteUserForm;

🏗️ 布局和 UI 组件系统

VGrove UI 提供了完整的布局系统和基础 UI 组件,支持响应式设计和灵活配置。

核心布局组件

VGroveLayout 主布局容器

VGroveLayout 是应用的主要布局容器,提供了完整的页面结构管理。

import { VGroveLayout } from '@gulibs/vgrove-ui';

// 侧边栏菜单配置
const menuItems = [
  {
    key: 'dashboard',
    label: '仪表板',
    href: '/dashboard',
    icon: <DashboardIcon />,
    badge: { content: '新', color: 'success' }
  },
  {
    key: 'users',
    label: '用户管理',
    icon: <UsersIcon />,
    children: [
      { key: 'user-list', label: '用户列表', href: '/users' },
      { key: 'user-roles', label: '角色管理', href: '/users/roles' },
      { key: 'user-permissions', label: '权限管理', href: '/users/permissions' }
    ]
  },
  {
    key: 'settings',
    label: '系统设置',
    href: '/settings',
    icon: <SettingsIcon />,
    requiredRoles: ['admin'] // 需要管理员角色
  }
];

function App() {
  return (
    <VGroveProvider>
      <VGroveLayout
        items={menuItems}
        onCollapsedChange={(collapsed) => {
          console.log('侧边栏折叠状态:', collapsed);
        }}
        navbarProps={{
          brandProps: {
            name: "我的应用",
            logo: <AppLogo />
          },
          userMenuProps: {
            user: currentUser,
            onLogout: handleLogout
          }
        }}
        sidebarProps={{
          defaultCollapsed: false,
          collapsible: true
        }}
        footerProps={{
          copyright: "© 2024 我的公司",
          links: [
            { label: '隐私政策', href: '/privacy' },
            { label: '服务条款', href: '/terms' }
          ]
        }}
        classNames={{
          layout: "min-h-screen",
          wrapper: "max-w-full",
          content: "p-6"
        }}
      >
        {/* 应用路由内容 */}
        <RouterProvider router={router} />
      </VGroveLayout>
    </VGroveProvider>
  );
}

VGroveLayout 特性:

  • 响应式布局: 自动适配不同屏幕尺寸
  • 可配置组件: 灵活配置导航栏、侧边栏、页脚
  • 实例管理: 防止多实例冲突的安全机制
  • 权限集成: 支持基于角色的菜单显示

VGroveNavbar 导航栏

import { VGroveNavbar } from '@gulibs/vgrove-ui';

function CustomNavbar() {
  return (
    <VGroveNavbar
      brandProps={{
        name: "VGrove UI",
        logo: <Logo />,
        onClick: () => navigate('/')
      }}
      searchProps={{
        placeholder: "搜索功能、用户、内容...",
        onSearch: (query) => handleSearch(query)
      }}
      userMenuProps={{
        user: {
          id: '1',
          name: '张三',
          email: '[email protected]',
          avatar: '/avatars/zhangsan.jpg'
        },
        menuItems: [
          { key: 'profile', label: '个人资料', href: '/profile' },
          { key: 'settings', label: '账号设置', href: '/settings' },
          { type: 'divider' },
          { key: 'logout', label: '退出登录', color: 'danger', onAction: handleLogout }
        ]
      }}
      notificationProps={{
        items: notifications,
        onMarkAsRead: handleMarkAsRead,
        onClearAll: handleClearAll
      }}
      themeSwitch={{
        variant: 'circle',
        showLabel: false
      }}
      classNames={{
        base: "bg-background/70 backdrop-blur-lg",
        wrapper: "px-6",
        brand: "font-bold"
      }}
    />
  );
}

VGroveSidebar 侧边栏

import { VGroveSidebar } from '@gulibs/vgrove-ui';

const sidebarMenus = [
  {
    key: 'main',
    type: 'group',
    title: '主要功能',
    items: [
      {
        key: 'dashboard',
        label: '仪表板',
        href: '/dashboard',
        icon: <DashboardIcon />,
        shortcut: 'Cmd+D'
      },
      {
        key: 'analytics',
        label: '数据分析',
        href: '/analytics',
        icon: <ChartIcon />,
        badge: { content: 'Beta', color: 'warning' }
      }
    ]
  },
  {
    key: 'management',
    type: 'group',
    title: '管理功能',
    items: [
      {
        key: 'users',
        label: '用户管理',
        icon: <UsersIcon />,
        children: [
          { key: 'user-list', label: '用户列表', href: '/users' },
          { key: 'user-groups', label: '用户组', href: '/users/groups' }
        ]
      }
    ]
  }
];

function CustomSidebar() {
  return (
    <VGroveSidebar
      menuItems={sidebarMenus}
      defaultCollapsed={false}
      collapsible={true}
      onCollapsedChange={(collapsed) => {
        localStorage.setItem('sidebarCollapsed', collapsed.toString());
      }}
      headerContent={
        <div className="px-3 py-2">
          <Brand name="VGrove UI" logo={<Logo />} />
        </div>
      }
      footerContent={
        <div className="px-3 py-2 text-xs text-default-400">
          版本 v1.0.0
        </div>
      }
      classNames={{
        base: "border-r border-divider",
        header: "border-b border-divider",
        content: "py-2",
        footer: "border-t border-divider"
      }}
    />
  );
}

VGroveFooter 页脚

import { VGroveFooter, DefaultFooter } from '@gulibs/vgrove-ui';

// 使用默认页脚
function SimpleFooter() {
  return (
    <DefaultFooter
      copyright="© 2024 我的公司"
      links={[
        { label: '关于我们', href: '/about' },
        { label: '联系我们', href: '/contact' },
        { label: '隐私政策', href: '/privacy' }
      ]}
    />
  );
}

// 自定义页脚
function CustomFooter() {
  return (
    <VGroveFooter className="bg-content1 border-t border-divider">
      <div className="max-w-7xl mx-auto px-6 py-8">
        <div className="grid grid-cols-1 md:grid-cols-4 gap-8">
          <div>
            <h3 className="font-semibold mb-3">产品</h3>
            <ul className="space-y-2 text-sm text-default-600">
              <li><a href="/features">功能特性</a></li>
              <li><a href="/pricing">价格方案</a></li>
              <li><a href="/api">API 文档</a></li>
            </ul>
          </div>

          <div>
            <h3 className="font-semibold mb-3">支持</h3>
            <ul className="space-y-2 text-sm text-default-600">
              <li><a href="/docs">文档中心</a></li>
              <li><a href="/support">技术支持</a></li>
              <li><a href="/community">社区论坛</a></li>
            </ul>
          </div>

          <div>
            <h3 className="font-semibold mb-3">公司</h3>
            <ul className="space-y-2 text-sm text-default-600">
              <li><a href="/about">关于我们</a></li>
              <li><a href="/careers">招聘信息</a></li>
              <li><a href="/contact">联系我们</a></li>
            </ul>
          </div>

          <div>
            <h3 className="font-semibold mb-3">关注我们</h3>
            <div className="flex space-x-3">
              <a href="#" className="text-default-400 hover:text-default-600">
                <TwitterIcon size={20} />
              </a>
              <a href="#" className="text-default-400 hover:text-default-600">
                <GithubIcon size={20} />
              </a>
              <a href="#" className="text-default-400 hover:text-default-600">
                <DiscordIcon size={20} />
              </a>
            </div>
          </div>
        </div>

        <div className="border-t border-divider mt-8 pt-6 text-center text-sm text-default-600">
          © 2024 我的公司. 保留所有权利.
        </div>
      </div>
    </VGroveFooter>
  );
}

页面组件

PageContainer 页面容器

PageContainer 为页面内容提供统一的容器和间距管理。

import { PageContainer, PageTitle } from '@gulibs/vgrove-ui';

function DashboardPage() {
  return (
    <PageContainer
      title="数据仪表板"
      subtitle="查看您的业务数据和分析报告"
      breadcrumbs={[
        { label: '首页', href: '/' },
        { label: '仪表板' }
      ]}
      actions={[
        <Button key="export" variant="flat" startContent={<ExportIcon />}>
          导出数据
        </Button>,
        <Button key="settings" variant="flat" startContent={<SettingsIcon />}>
          设置
        </Button>
      ]}
      classNames={{
        title: "text-3xl font-bold",
        main: "mt-6 space-y-6"
      }}
    >
      {/* 页面内容 */}
      <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
        <StatCard title="总用户数" value="1,234" trend="+12%" />
        <StatCard title="活跃用户" value="856" trend="+8%" />
        <StatCard title="新增用户" value="42" trend="+15%" />
      </div>

      <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
        <ChartCard title="用户增长趋势" />
        <ChartCard title="活跃度分析" />
      </div>
    </PageContainer>
  );
}

PageTitle 页面标题

PageTitle 组件提供 SEO 友好的页面标题管理和面包屑导航。

import { PageTitle } from '@gulibs/vgrove-ui';

function UserProfilePage() {
  return (
    <>
      <PageTitle
        title="用户资料"
        subtitle="管理您的个人信息和偏好设置"
        breadcrumbs={[
          { label: '首页', href: '/' },
          { label: '账户', href: '/account' },
          { label: '用户资料' }
        ]}
        meta={{
          description: '用户个人资料管理页面',
          keywords: ['用户', '资料', '设置', '个人信息']
        }}
      />

      {/* 页面内容 */}
      <div className="max-w-4xl mx-auto p-6">
        {/* ... */}
      </div>
    </>
  );
}

基础 UI 组件

Brand 品牌组件

Brand 组件用于显示应用品牌信息,支持 Logo 和名称的灵活配置。

import { Brand } from '@gulibs/vgrove-ui';

function AppBrand() {
  return (
    <Brand
      name="VGrove UI"
      logo={<Logo className="w-8 h-8" />}
      collapsed={sidebarCollapsed}
      onClick={() => navigate('/')}
      classNames={{
        base: "cursor-pointer hover:opacity-80 transition-opacity",
        logo: "mr-3",
        nameWrapper: "overflow-hidden",
        name: "font-bold text-lg truncate"
      }}
    />
  );
}

// 仅显示 Logo 的紧凑模式
function CompactBrand() {
  return (
    <Brand
      logo={<Logo className="w-10 h-10" />}
      collapsed={true}
      onClick={() => navigate('/')}
    />
  );
}

ThemeSwitch 主题切换器

ThemeSwitch 提供了多种主题切换界面,支持明暗主题和系统主题。

import { ThemeSwitch } from '@gulibs/vgrove-ui';

function ThemeControls() {
  return (
    <div className="flex items-center space-x-4">
      {/* 开关样式 */}
      <ThemeSwitch
        variant="switch"
        size="md"
        showLabel={true}
      />

      {/* 下拉菜单样式 */}
      <ThemeSwitch
        variant="dropdown"
        enableSystem={true}
        onThemeChange={(theme, resolvedTheme) => {
          console.log('主题变更:', theme, '解析主题:', resolvedTheme);
        }}
      />

      {/* 圆形按钮样式 */}
      <ThemeSwitch
        variant="circle"
        size="lg"
      />

      {/* 按钮组样式 */}
      <ThemeSwitch
        variant="buttons"
        enableSystem={true}
        showLabel={true}
      />
    </div>
  );
}

Collapse 折叠组件

Collapse 组件提供了折叠 / 展开功能,常用于侧边栏控制。

import { Collapse } from '@gulibs/vgrove-ui';

function SidebarToggle() {
  const [collapsed, setCollapsed] = useState(false);

  return (
    <Collapse
      collapsed={collapsed}
      onCollapsedChange={setCollapsed}
      className="p-2 hover:bg-default-100 rounded-lg transition-colors"
      icon={<MenuIcon />}
      tooltip={{
        content: collapsed ? '展开侧边栏' : '折叠侧边栏',
        placement: 'right'
      }}
    />
  );
}

高级组件

Listbox 列表选择器

Listbox 组件提供了强大的列表选择功能,支持键盘导航和多选。

import { Listbox, ListboxItem, ListboxSection } from '@gulibs/vgrove-ui';

function UserListbox() {
  const [selected, setSelected] = useState(new Set(['user1']));

  return (
    <Listbox
      aria-label="用户选择"
      variant="flat"
      disallowEmptySelection
      selectionMode="multiple"
      selectedKeys={selected}
      onSelectionChange={setSelected}
      className="max-w-xs"
    >
      <ListboxSection title="管理员">
        <ListboxItem
          key="admin1"
          startContent={<Avatar size="sm" src="/avatars/admin1.jpg" />}
          description="系统管理员"
        >
          张管理员
        </ListboxItem>
        <ListboxItem
          key="admin2"
          startContent={<Avatar size="sm" src="/avatars/admin2.jpg" />}
          description="内容管理员"
        >
          李管理员
        </ListboxItem>
      </ListboxSection>

      <ListboxSection title="普通用户">
        <ListboxItem
          key="user1"
          startContent={<Avatar size="sm" src="/avatars/user1.jpg" />}
          description="活跃用户"
        >
          王用户
        </ListboxItem>
        <ListboxItem
          key="user2"
          startContent={<Avatar size="sm" src="/avatars/user2.jpg" />}
          description="新用户"
        >
          刘用户
        </ListboxItem>
      </ListboxSection>
    </Listbox>
  );
}

响应式布局配置

布局设置

import { VGroveProvider } from '@gulibs/vgrove-ui';

function App() {
  const layoutSettings = {
    // 侧边栏配置
    sidebar: {
      width: 280,           // 展开宽度
      collapsedWidth: 72,   // 折叠宽度
      collapsible: true,    // 是否可折叠
      collapsed: false,     // 默认折叠状态
      breakpoint: 'lg'      // 响应式断点
    },

    // 导航栏配置
    navbar: {
      height: 64,           // 导航栏高度
      fixed: true,          // 是否固定
      transparent: false,   // 是否透明
      blur: true           // 是否启用模糊效果
    },

    // 页脚配置
    footer: {
      height: 60,          // 页脚高度
      fixed: false,        // 是否固定
      show: true          // 是否显示
    },

    // 内容区域配置
    content: {
      padding: 24,         // 内容区域内边距
      maxWidth: '100%',    // 最大宽度
      centered: false      // 是否居中
    }
  };

  return (
    <VGroveProvider settings={layoutSettings}>
      <VGroveLayout items={menuItems}>
        <Router />
      </VGroveLayout>
    </VGroveProvider>
  );
}

样式定制

CSS 变量定制

/* 自定义 CSS 变量 */
:root {
  /* 布局尺寸 */
  --vgrove-sidebar-width: 280px;
  --vgrove-sidebar-collapsed-width: 72px;
  --vgrove-navbar-height: 64px;
  --vgrove-footer-height: 60px;

  /* 间距 */
  --vgrove-content-padding: 24px;
  --vgrove-page-max-width: 1200px;

  /* 过渡动画 */
  --vgrove-transition-duration: 200ms;
  --vgrove-transition-timing: ease-in-out;

  /* 阴影 */
  --vgrove-sidebar-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
  --vgrove-navbar-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

/* 暗色主题定制 */
.dark {
  --vgrove-sidebar-shadow: 2px 0 8px rgba(0, 0, 0, 0.3);
  --vgrove-navbar-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}

Tailwind CSS 配置扩展

// tailwind.config.js
module.exports = {
  // ... 其他配置
  theme: {
    extend: {
      spacing: {
        'sidebar': 'var(--vgrove-sidebar-width)',
        'sidebar-collapsed': 'var(--vgrove-sidebar-collapsed-width)',
        'navbar': 'var(--vgrove-navbar-height)',
        'footer': 'var(--vgrove-footer-height)'
      },
      transitionDuration: {
        'vgrove': 'var(--vgrove-transition-duration)'
      },
      boxShadow: {
        'sidebar': 'var(--vgrove-sidebar-shadow)',
        'navbar': 'var(--vgrove-navbar-shadow)'
      }
    }
  }
}

🌐 国际化和主题系统

VGrove UI 提供了完整的国际化 (i18n) 支持和灵活的主题系统,让您的应用能够轻松适配不同语言和视觉风格。

国际化系统

I18nMessage 国际化消息组件

I18nMessage 组件用于显示多语言文本内容,支持变量插值和格式化。

import { I18nMessage } from '@gulibs/vgrove-ui';

function WelcomeSection() {
  const user = { name: '张三', points: 1250 };

  return (
    <div className="space-y-4">
      {/* 基础消息 */}
      <I18nMessage
        id="welcome.title"
        defaultValue="欢迎使用 VGrove UI"
      />

      {/* 带变量插值的消息 */}
      <I18nMessage
        id="welcome.greeting"
        defaultValue="您好,{name}!"
        values={{ name: user.name }}
      />

      {/* 复杂消息格式化 */}
      <I18nMessage
        id="user.points"
        defaultValue="您当前有 {points, number} 积分"
        values={{ points: user.points }}
      />

      {/* 带有HTML的富文本消息 */}
      <I18nMessage
        id="terms.agreement"
        defaultValue="我同意 <a href='/terms'>用户协议</a> 和 <a href='/privacy'>隐私政策</a>"
        values={{
          termsLink: (text) => <a href="/terms" className="text-primary">{text}</a>,
          privacyLink: (text) => <a href="/privacy" className="text-primary">{text}</a>
        }}
      />

      {/* 带回退的消息 */}
      <I18nMessage
        id="feature.beta"
        fallback="此功能正在测试中"
      />
    </div>
  );
}

I18nSwitch 语言切换组件

import { I18nSwitch } from '@gulibs/vgrove-ui';

function LanguageControls() {
  return (
    <div className="flex items-center space-x-4">
      {/* 下拉菜单样式 */}
      <I18nSwitch
        variant="dropdown"
        supportedLanguages={[
          { code: 'zh-CN', name: '简体中文', flag: '🇨🇳' },
          { code: 'en-US', name: 'English', flag: '🇺🇸' },
          { code: 'ja-JP', name: '日本語', flag: '🇯🇵' }
        ]}
        onLanguageChange={(language) => {
          console.log('语言切换到:', language);
        }}
      />

      {/* 按钮组样式 */}
      <I18nSwitch
        variant="buttons"
        supportedLanguages={[
          { code: 'zh-CN', name: '中', flag: '🇨🇳' },
          { code: 'en-US', name: 'EN', flag: '🇺🇸' }
        ]}
      />
    </div>
  );
}

主题系统

useTheme Hook

import { useTheme } from '@gulibs/vgrove-ui';

function ThemeControls() {
  const {
    theme,          // 当前主题设置
    resolvedTheme,  // 解析后的实际主题
    setTheme,       // 设置主题函数
    toggleTheme,    // 切换明暗主题
    isDark,         // 是否为暗色主题
    isSystem        // 是否跟随系统
  } = useTheme();

  return (
    <div className="space-y-4">
      <div className="flex space-x-2">
        <Button
          onClick={() => setTheme('light')}
          variant={theme === 'light' ? 'solid' : 'flat'}
        >
          浅色
        </Button>

        <Button
          onClick={() => setTheme('dark')}
          variant={theme === 'dark' ? 'solid' : 'flat'}
        >
          深色
        </Button>

        <Button
          onClick={() => setTheme('system')}
          variant={theme === 'system' ? 'solid' : 'flat'}
        >
          跟随系统
        </Button>
      </div>

      <Button onClick={toggleTheme}>
        切换到{isDark ? '浅色' : '深色'}主题
      </Button>
    </div>
  );
}

ThemeSwitch 主题切换器

import { ThemeSwitch } from '@gulibs/vgrove-ui';

function ThemeSelectors() {
  return (
    <div className="space-x-4">
      {/* 开关样式 */}
      <ThemeSwitch variant="switch" showLabel={true} />

      {/* 下拉菜单样式 */}
      <ThemeSwitch variant="dropdown" enableSystem={true} />

      {/* 圆形按钮样式 */}
      <ThemeSwitch variant="circle" />

      {/* 按钮组样式 */}
      <ThemeSwitch variant="buttons" enableSystem={true} />
    </div>
  );
}

🎣 Hooks 和高级功能

VGrove UI 提供了丰富的自定义 Hooks 和高级功能,帮助您构建更强大和灵活的应用。

VGrove 核心 Hooks

useVGrove - 核心上下文 Hook

useVGrove 提供对 VGrove 应用上下文的访问,包括布局设置和全局状态管理。

import { useVGrove } from '@gulibs/vgrove-ui';

function LayoutController() {
  const {
    settings,           // 当前布局设置
    updateSettings,     // 更新设置函数
    resetSettings       // 重置设置函数
  } = useVGrove();

  const toggleSidebar = () => {
    updateSettings({
      sidebar: {
        ...settings.sidebar,
        collapsed: !settings.sidebar?.collapsed
      }
    });
  };

  return (
    <div className="space-y-4">
      <Switch
        isSelected={!settings.sidebar?.collapsed}
        onValueChange={toggleSidebar}
      >
        侧边栏: {settings.sidebar?.collapsed ? '已折叠' : '已展开'}
      </Switch>

      <Button onClick={resetSettings} color="warning">
        重置布局设置
      </Button>
    </div>
  );
}

表单相关 Hooks

useWatch / useFieldValue - 表单字段监听

import { useWatch, useFieldValue, useFieldsWatcher } from '@gulibs/vgrove-ui';

function FormWatcher() {
  // 监听单个字段
  const username = useFieldValue('username');

  // 监听多个字段
  const [firstName, lastName] = useWatch(['firstName', 'lastName']);

  // 监听字段状态
  const { allFilled, anyFilled } = useFieldsWatcher(['name', 'email']);

  return (
    <div className="space-y-2">
      <p>用户名: {username || '未填写'}</p>
      <p>全名: {firstName && lastName ? `${firstName} ${lastName}` : '未完整'}</p>
      <p>状态: {allFilled ? '完成' : anyFilled ? '部分完成' : '未开始'}</p>
    </div>
  );
}

高级功能

调试配置

import { updateDebugConfig } from '@gulibs/vgrove-ui';

// 开发环境启用调试
if (process.env.NODE_ENV === 'development') {
  updateDebugConfig({
    enabled: true,
    auth: true,       // 认证调试
    loader: true,     // Loader 调试
    action: true,     // Action 调试
    routing: true,    // 路由调试
    performance: true // 性能监控
  });
}

存储管理

import { useStorage } from '@gulibs/vgrove-ui';

function UserSettings() {
  const {
    value: settings,
    setValue: setSettings,
    remove: removeSettings
  } = useStorage('user-settings', {
    theme: 'light',
    language: 'zh-CN'
  });

  return (
    <div className="space-y-4">
      <Select
        value={settings.theme}
        onChange={(e) => setSettings({
          ...settings,
          theme: e.target.value
        })}
      >
        <option value="light">浅色</option>
        <option value="dark">深色</option>
      </Select>

      <Button onClick={removeSettings} color="warning">
        重置设置
      </Button>
    </div>
  );
}

📚 类型定义和 API 参考

表单相关 API

函数和工具

// 创建 FormInstance
function createFormInstance<TFieldValues extends FieldValues = FieldValues>(
  formReturn: UseFormReturn<TFieldValues>
): FormInstance<TFieldValues>

// 增强的 useForm Hook
function useForm<TFieldValues extends FieldValues = FieldValues>(
  options?: CustomUseFormProps<TFieldValues>
): FormInstance<TFieldValues>

// 注册全局 FormInstance
function registerFormInstance(instance: FormInstance<any>, key?: string): void

// 注销全局 FormInstance
function unregisterFormInstance(key?: string): void

// 获取全局 FormInstance
function getFormInstance(key?: string): FormInstance<any> | null

// 字段值监听
function useFieldValue<TFieldValues extends FieldValues = FieldValues>(
  name: FieldPath<TFieldValues>,
  control?: Control<TFieldValues>
): any

// 多字段监听
function useFieldsWatcher<TFieldValues extends FieldValues = FieldValues>(
  names: FieldPath<TFieldValues>[],
  control?: Control<TFieldValues>
): FieldsWatcherResult

// 字段更新工具
function useFieldUpdate<T = Element>(name: string): FieldUpdateResult<T>

// 获取表单上下文 (返回 FormInstance)
function useFormContext<TFieldValues extends FieldValues = FieldValues>(): FormInstance<TFieldValues>

// 可选获取表单上下文
function useFormContextOptional<TFieldValues extends FieldValues = FieldValues>(): FormInstance<TFieldValues> | null

// 检测是否在 FormProvider 内部
function useIsInsideFormProvider(): boolean

// 获取可用的 FormInstance
function useAvailableFormInstance<TFieldValues extends FieldValues = FieldValues>(): FormInstance<TFieldValues> | null

核心类型

用户和认证相关

// 基础用户类型
interface BaseUser {
  id: string | number;
  username?: string;
  email?: string;
  name?: string;
  avatar?: string;
  roles?: string[];
  permissions?: string[];
  [key: string]: any;
}

// 认证配置
interface AuthConfig {
  authCheck: () => Promise<BaseUser | null> | BaseUser | null;
  roleCheck?: (user: BaseUser, roles: string[]) => boolean;
  permissionCheck?: (user: BaseUser, permissions: string[]) => boolean;
  loginPath?: string;
  forbiddenPath?: string;
  publicPaths?: string[];
}

表单相关类型

// FormInstance 接口定义
interface FormInstance<TFieldValues extends FieldValues = FieldValues>
  extends UseFormReturn<TFieldValues> {
  // 基础方法
  getFieldValue: (name: FieldPath<TFieldValues>) => any;
  getFieldsValue: () => TFieldValues;
  setFieldValue: (name: FieldPath<TFieldValues>, value: any) => void;
  setFieldsValue: (values: Partial<TFieldValues>) => void;
  resetFields: () => void;

  // 验证方法
  validateFields: () => Promise<boolean>;
  setFieldError: (name: FieldPath<TFieldValues>, error: any) => void;
  clearFieldErrors: (names?: FieldPath<TFieldValues>[]) => void;
}

// 自定义 useForm 配置
interface CustomUseFormProps<TFieldValues extends FieldValues = FieldValues>
  extends UseFormProps<TFieldValues> {
  // 是否注册为全局默认实例
  registerAsGlobal?: boolean;
  // 注册的键名(如果不提供则作为默认实例)
  globalKey?: string;
}

// 表单配置
interface FormProps<TFieldValues extends FieldValues = FieldValues> {
  mode?: 'all' | 'onBlur' | 'onChange' | 'onSubmit';
  watchNames?: FieldPath<TFieldValues> | FieldPath<TFieldValues>[];
  footer?: false | FormFooterConfig;
  form?: FormInstance<TFieldValues>; // 支持传入 FormInstance
  onFinish?: SubmitHandler<TFieldValues>;
  onFinishError?: SubmitErrorHandler<TFieldValues>;
}

// 表单页脚配置
interface FormFooterConfig {
  resetProps?: Omit<ButtonProps, "children">;
  resetText?: React.ReactNode;
  submitProps?: Omit<ButtonProps, "children">;
  submitText?: React.ReactNode;
  hiddenReset?: boolean;
  onReset?: () => void;
}

// 表单项配置
interface FormItemProps {
  name: string;
  label?: React.ReactNode;
  required?: boolean;
  help?: React.ReactNode;
  tooltip?: TooltipProps;
  rules?: RegisterOptions;
  layout?: 'vertical' | 'horizontal';
  validateStatus?: 'error' | 'warning' | 'success';
}

// 表单字段监听器返回类型
interface FieldsWatcherResult {
  values: any[];
  allFilled: boolean;
  anyFilled: boolean;
}

// 表单字段更新工具返回类型
interface FieldUpdateResult<T = Element> {
  isInvalid: () => boolean;
  handle: (handler: EventHandler<T>) => (event: FormEvent<T>) => void;
  clear: () => void;
}

布局相关类型

// 布局设置
interface LayoutSettings {
  sidebar?: {
    width?: number;
    collapsedWidth?: number;
    collapsible?: boolean;
    collapsed?: boolean;
    breakpoint?: 'sm' | 'md' | 'lg' | 'xl';
  };
  navbar?: {
    height?: number;
    fixed?: boolean;
    transparent?: boolean;
    blur?: boolean;
  };
  footer?: {
    height?: number;
    fixed?: boolean;
    show?: boolean;
  };
}

// 菜单项类型
interface SidebarMenuItem {
  key: string;
  label: React.ReactNode;
  href?: string;
  icon?: React.ReactNode;
  badge?: BadgeProps;
  children?: SidebarMenuItem[];
  requiredRoles?: string[];
  requiredPermissions?: string[];
}

主题相关类型

// 主题模式
type ThemeMode = 'light' | 'dark' | 'system';
type ResolvedTheme = 'light' | 'dark';

// 主题配置
interface ThemeConfig {
  defaultTheme?: ThemeMode;
  enableSystem?: boolean;
  attribute?: string;
  storageKey?: string;
  themes?: string[];
}

// 主题切换器配置
interface ThemeSwitchProps {
  variant?: 'switch' | 'dropdown' | 'circle' | 'buttons';
  size?: 'sm' | 'md' | 'lg';
  showLabel?: boolean;
  enableSystem?: boolean;
  onThemeChange?: (theme: ThemeMode, resolvedTheme: ResolvedTheme) => void;
}

💡 最佳实践和示例

项目结构建议

src/
├── components/          # 通用组件
│   ├── ui/             # UI 基础组件
│   ├── forms/          # 表单组件
│   └── layout/         # 布局组件
├── pages/              # 页面组件
├── hooks/              # 自定义 Hooks
├── utils/              # 工具函数
├── types/              # 类型定义
├── constants/          # 常量定义
├── locales/            # 国际化资源
│   ├── zh-CN.json
│   ├── en-US.json
│   └── index.ts
└── config/             # 配置文件
    ├── auth.ts         # 认证配置
    ├── routes.ts       # 路由配置
    └── theme.ts        # 主题配置

完整应用示例

// App.tsx
import React from 'react';
import {
  VGroveProvider,
  VGroveLayout,
  quickSetup,
  createBrowserRouter,
  RouterProvider
} from '@gulibs/vgrove-ui';
import { authConfig } from './config/auth';
import { menuItems } from './config/menu';

// 快速设置认证
const { authLoader, loginAction, logoutAction } = quickSetup(authConfig);

// 创建路由
const router = createBrowserRouter([
  {
    path: '/login',
    element: <LoginPage />,
    action: loginAction
  },
  {
    path: '/',
    element: <HomePage />,
    loader: authLoader
  },
  {
    path: '/dashboard',
    element: <DashboardPage />,
    loader: authLoader
  }
]);

function App() {
  return (
    <VGroveProvider
      settings={{
        sidebar: { collapsible: true },
        navbar: { fixed: true }
      }}
      themeConfig={{
        defaultTheme: 'system',
        enableSystem: true
      }}
    >
      <VGroveLayout
        items={menuItems}
        onCollapsedChange={(collapsed) => {
          localStorage.setItem('sidebar-collapsed', String(collapsed));
        }}
      >
        <RouterProvider router={router} />
      </VGroveLayout>
    </VGroveProvider>
  );
}

export default App;

性能优化建议

// 1. 使用 React.memo 优化组件渲染
const OptimizedComponent = React.memo(({ data }) => {
  return <div>{data.title}</div>;
});

// 2. 使用 useMemo 优化计算
const ExpensiveComponent = ({ items }) => {
  const processedItems = useMemo(() => {
    return items.map(item => ({
      ...item,
      processed: heavyComputation(item)
    }));
  }, [items]);

  return <ItemList items={processedItems} />;
};

// 3. 使用 useCallback 优化事件处理
const FormComponent = ({ onSubmit }) => {
  const handleSubmit = useCallback((data) => {
    onSubmit(data);
  }, [onSubmit]);

  return <Form onFinish={handleSubmit} />;
};

🛠️ 开发和构建

本地开发

# 克隆项目
git clone https://github.com/your-org/your-project.git
cd your-project

# 安装依赖
npm install

# 启动开发服务器
npm run dev

# 启动 Storybook (如果有)
npm run storybook

构建和部署

# 构建生产版本
npm run build

# 预览构建结果
npm run preview

# 运行测试
npm test

# 运行类型检查
npm run type-check

# 运行代码检查
npm run lint

🤝 贡献指南

提交代码

  1. Fork 项目到您的 GitHub 账户
  2. 创建功能分支:git checkout -b feature/amazing-feature
  3. 提交更改:git commit -m 'Add amazing feature'
  4. 推送到分支:git push origin feature/amazing-feature
  5. 创建 Pull Request

开发规范

  • 遵循现有的代码风格和命名约定
  • 为新功能添加适当的类型定义
  • 编写单元测试覆盖新功能
  • 更新相关文档和示例

📄 许可证

本项目基于 MIT 许可证开源。详见 LICENSE 文件。


让我们一起构建更好的 React 应用! 🚀