diff --git a/.eslintignore b/.eslintignore index f4289299..d3c3e56c 100644 --- a/.eslintignore +++ b/.eslintignore @@ -5,3 +5,4 @@ public .cache .eslintrc.js *.d.ts +docusaurus/ diff --git a/.eslintrc.js b/.eslintrc.js index 33bc6071..6273e435 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -3,7 +3,8 @@ module.exports = { extends: ['airbnb-typescript', 'plugin:jest/recommended', 'plugin:prettier/recommended'], plugins: ['prettier', '@typescript-eslint', 'jest'], parserOptions: { - project: './tsconfig.json', + project: ['./packages/*/tsconfig.json'], + tsconfigRootDir: __dirname, }, env: { @@ -12,7 +13,45 @@ module.exports = { 'jest/globals': true, }, + settings: { + jest: { + version: 27, + }, + }, + + overrides: [ + { + files: ['*.story.tsx', '*.story.ts'], + parserOptions: { + project: null, + }, + rules: { + '@typescript-eslint/dot-notation': 'off', + '@typescript-eslint/no-implied-eval': 'off', + '@typescript-eslint/no-throw-literal': 'off', + '@typescript-eslint/return-await': 'off', + }, + }, + { + files: ['scripts/**/*.ts', 'configs/**/*.ts'], + parserOptions: { + project: null, + }, + rules: { + '@typescript-eslint/dot-notation': 'off', + '@typescript-eslint/no-implied-eval': 'off', + '@typescript-eslint/no-throw-literal': 'off', + '@typescript-eslint/return-await': 'off', + }, + }, + ], + rules: { + // Disable deprecated rules + 'react/jsx-filename-extension': 'off', + 'import/extensions': 'off', + 'jsx-a11y/click-events-have-key-events': 'off', + 'max-len': [ 'error', 100, @@ -53,6 +92,11 @@ module.exports = { // disabled for condition && someFunc() 'no-unused-expressions': 'off', + // Disable deprecated/removed rules in newer ESLint versions + 'react/prop-types': 'off', + 'react/no-unused-prop-types': 'off', + 'react/button-has-type': 'off', + // backend developers like _, no need to transform data all the time camelcase: 'off', diff --git a/.github/workflows/docsearch.yml b/.github/workflows/docsearch.yml new file mode 100644 index 00000000..cb9295b3 --- /dev/null +++ b/.github/workflows/docsearch.yml @@ -0,0 +1,51 @@ +name: Update Algolia DocSearch Index + +on: + # 1. Automatically triggered on push to main branch (most common) + push: + branches: + - main + - master # Add master branch if you use it + + # 2. Manual trigger (important!) + workflow_dispatch: + # Optional: Add an input field for manually entering the site URL (remove these lines if not needed) + inputs: + site_url: + description: 'Site URL (defaults to start_urls from config)' + required: false + default: '' + +jobs: + algolia: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Read docsearch.json configuration + id: algolia_config + run: | + CONFIG=$(cat docusaurus/algolia-crawler-config.json | jq -c .) + echo "config=$CONFIG" >> $GITHUB_OUTPUT + + - name: Override start_urls in config if URL is manually entered (optional feature) + if: github.event.inputs.site_url != '' + run: | + NEW_CONFIG=$(jq --arg url "${{ github.event.inputs.site_url }}" \ + '.start_urls[0].url = $url' docsearch.json) + echo "$NEW_CONFIG" > docsearch.json.tmp + mv docsearch.json.tmp docsearch.json + echo "Replaced with manually entered URL: ${{ github.event.inputs.site_url }}" + + - name: Run Algolia official crawler + env: + APPLICATION_ID: ${{ secrets.APPLICATION_ID }} # Your Application ID + API_KEY: ${{ secrets.API_KEY }} # Your Admin API Key (write permission) + CONFIG: ${{ steps.algolia_config.outputs.config }} + run: | + docker run --rm \ + --env APPLICATION_ID=${APPLICATION_ID} \ + --env API_KEY=${API_KEY} \ + --env "CONFIG=${CONFIG}" \ + algolia/docsearch-scraper diff --git a/.gitignore b/.gitignore index c40b55cd..ae3537d6 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,7 @@ docs/.next !docs/lib # history -.history/ \ No newline at end of file +.history/ + +**/__snapshots__/ +coverage/ diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..085cea7b --- /dev/null +++ b/.npmrc @@ -0,0 +1,4 @@ +shamefully-hoist=true +strict-peer-dependencies=false +auto-install-peers=true +node-linker=hoisted diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..03d08e7c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,179 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +Kube Design is a comprehensive React component library originally built for KubeSphere Console, featuring components, hooks, and icons. The project uses a monorepo structure with multiple packages and documentation platforms. + +## Architecture + +### Monorepo Structure +- **Packages**: Located in `/packages/` with individual build configurations +- **Documentation**: Multiple platforms (Next.js docs, Nextra, Astro) +- **Build System**: Custom TypeScript/Rollup-based build with ESM/CJS output +- **Testing**: Vitest with React Testing Library and jest-axe for accessibility + +### Key Packages +- `@kubed/components` - Main React component library (50+ components) +- `@kubed/icons` - Icon system with generation pipeline +- `@kubed/hooks` - React utility hooks +- `@kubed/charts` - Chart components +- `@kubed/code-editor` - Code editor component +- `@kubed/diff-viewer` - Diff viewer component +- `@kubed/log-viewer` - Log viewer component + +## Development Commands + +### Core Commands +```bash +# Install dependencies +pnpm install + +# Build all packages +pnpm build:all + +# Build specific package +pnpm build components + +# Development with live reload +pnpm storybook # Storybook on port 7001 +pnpm start:docs # Next.js docs development + +# Linting and Type Checking +pnpm lint # ESLint for all packages +pnpm typecheck # TypeScript checking +pnpm test # Run all tests + syncpack + +# Testing +pnpm test:watch # Watch mode testing +pnpm test:coverage # Coverage reports +pnpm test:ui # Vitest UI interface +``` + +### Package-Specific Commands +```bash +# Build individual packages +pnpm build components # Build components package +pnpm build icons # Build icons package +pnpm build:docs # Build documentation + +# Generate documentation +pnpm docs:docgen # Auto-generate component docs +``` + +## Build System + +### Build Configuration +- **Output Formats**: ESM (`esm/`), CJS (`cjs/`), TypeScript definitions (`lib/`) +- **Builder**: Custom TypeScript/Rollup configuration in `/scripts/` +- **Source**: Each package has `src/` → builds to `esm/` + `cjs/` + `lib/` + +### Build Options +```bash +# Available build flags +--analyze # Generate bundle analysis +--sourcemap # Include source maps (default: true) +--minify # Minify UMD files (default: false) +--formats # Output formats: es, cjs (default: both) +``` + +## Testing + +### Test Setup +- **Framework**: Vitest with React Testing Library +- **Environment**: jsdom with styled-components support +- **Accessibility**: jest-axe for a11y testing +- **Coverage**: Reports in HTML, JSON, and text formats + +### Test Patterns +- Component tests: `*.test.tsx` alongside components +- Story files: `*.story.tsx` for Storybook +- Test utilities: `@kubed/tests` package with shared helpers + +### Running Tests +```bash +# Single test file +vitest run Button.test.tsx + +# Test with coverage for specific package +vitest run --coverage.include="packages/components/src/Button/**/*" + +# Test specific pattern +vitest run Alert +``` + +## Documentation Platforms + +### 1. Next.js Documentation (`/docs/`) +- **Tech**: Next.js 14 with MDX, i18n support +- **Run**: `pnpm start:docs` (port 3000) +- **Build**: `pnpm build:docs` + +### 2. Nextra Documentation (`/nextra/`) +- **Tech**: Next.js 15 with Nextra 4 +- **Run**: `pnpm dev` from /nextra directory +- **Features**: Pagefind search, Tailwind CSS + +### 3. Astro Documentation (`/astro/`) +- **Tech**: Astro with React components +- **Run**: `pnpm dev` from /astro directory + +## Package Development + +### Creating New Components +1. Create component in `packages/components/src/[ComponentName]/` +2. Add `.story.tsx` for Storybook +3. Add `.test.tsx` for tests +4. Export from `packages/components/src/index.ts` +5. Run `pnpm build components` to build + +### Component Structure +``` +ComponentName/ +├── ComponentName.tsx # Main component +├── ComponentName.styles.ts # Styled components +├── ComponentName.story.tsx # Storybook story +├── ComponentName.test.tsx # Component tests +└── index.ts # Re-export +``` + +### Icon System +- **Source**: SVG files in `packages/icons/source/` +- **Generation**: `pnpm build icons` generates React components +- **Output**: Multiple icon sets (fill, duotone variants) + +## Tooling + +### Dependencies +- **Package Manager**: pnpm with workspace support +- **Styling**: styled-components with theme system +- **TypeScript**: Strict mode with ES2015 target +- **Linting**: ESLint with Airbnb config + TypeScript +- **Formatting**: Prettier + +### Configuration Files +- **TypeScript**: `tsconfig.base.json` (base), individual package configs +- **Build**: `scripts/utils/build-package.ts` (shared build logic) +- **Testing**: `vitest.config.ts` (test configuration) +- **Linting**: ESLint config at root level + +## Environment Setup + +### Node.js Requirements +- Node.js 16+ (recommended: latest LTS) +- pnpm 8+ (package manager) + +### IDE Integration +- TypeScript path mapping configured for `@kubed/*` packages +- Storybook for component development +- Vitest for test running + +## Common Development Flow + +1. **Start development**: `pnpm storybook` for component work +2. **Create component**: Add to `packages/components/src/` +3. **Test**: `pnpm test:watch` for TDD +4. **Build**: `pnpm build [package]` to verify builds +5. **Document**: Update MDX files in `/docs/` or `/nextra/` +6. **Check**: `pnpm lint && pnpm typecheck` before commit \ No newline at end of file diff --git a/configs/.storybook/main.js b/configs/.storybook/main.js index 5fbc335e..b41debd8 100644 --- a/configs/.storybook/main.js +++ b/configs/.storybook/main.js @@ -1,7 +1,6 @@ import { dirname, join } from 'path'; /* eslint-disable no-param-reassign */ const path = require('path'); -const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin').default; module.exports = { stories: [ @@ -10,7 +9,6 @@ module.exports = { ], addons: [ - getAbsolutePath('storybook-addon-turbo-build'), getAbsolutePath('@storybook/addon-docs'), ], @@ -18,26 +16,20 @@ module.exports = { reactDocgen: true, }, - webpackFinal: async (config) => { - config.resolve = { - ...config.resolve, - plugins: [ - ...(config.resolve.plugins || []), - new TsconfigPathsPlugin({ - extensions: ['.ts', '.tsx', '.js'], - configFile: path.join(__dirname, '../../tsconfig.json'), - }), - ], + async viteFinal(config) { + // Ensure ESM resolution for monorepo packages + config.resolve = config.resolve || {}; + config.resolve.alias = { + ...config.resolve.alias, + '@kubed/icons': path.resolve(__dirname, '../../packages/icons/dist/index.mjs'), + '@kubed/components': path.resolve(__dirname, '../../packages/components/src'), + '@kubed/hooks': path.resolve(__dirname, '../../packages/hooks/src'), }; - - // Turn off docgen plugin as it breaks bundle with displayName - // config.plugins.pop(); - return config; }, framework: { - name: getAbsolutePath('@storybook/nextjs'), + name: getAbsolutePath('@storybook/react-vite'), options: {}, }, diff --git a/configs/jest/enzyme.setup.js b/configs/jest/enzyme.setup.js deleted file mode 100644 index 31cd985d..00000000 --- a/configs/jest/enzyme.setup.js +++ /dev/null @@ -1,8 +0,0 @@ -const Enzyme = require('enzyme'); -const Adapter = require('@wojtekmaj/enzyme-adapter-react-17'); -const React = require('react'); - -// Fix broken layout effects on testing environments -React.useLayoutEffect = React.useEffect; - -Enzyme.configure({ adapter: new Adapter() }); diff --git a/configs/jest/jsdom.mocks.js b/configs/jest/jsdom.mocks.js deleted file mode 100644 index 97cb9943..00000000 --- a/configs/jest/jsdom.mocks.js +++ /dev/null @@ -1,13 +0,0 @@ -Object.defineProperty(window, 'matchMedia', { - writable: true, - value: jest.fn().mockImplementation((query) => ({ - matches: false, - media: query, - onchange: null, - addListener: jest.fn(), - removeListener: jest.fn(), - addEventListener: jest.fn(), - removeEventListener: jest.fn(), - dispatchEvent: jest.fn(), - })), -}); diff --git a/configs/vitest/setup.ts b/configs/vitest/setup.ts new file mode 100644 index 00000000..a23d6a7e --- /dev/null +++ b/configs/vitest/setup.ts @@ -0,0 +1,36 @@ +import '@testing-library/jest-dom/vitest'; +import { afterEach, vi, expect } from 'vitest'; +import { cleanup } from '@testing-library/react'; +import { toHaveNoViolations } from 'jest-axe'; + + +// Add jest-axe matchers +expect.extend(toHaveNoViolations); + +afterEach(() => { + cleanup(); + + vi.restoreAllMocks(); +}); + +// Mock matchMedia +Object.defineProperty(window, 'matchMedia', { + writable: true, + value: (query: string) => ({ + matches: false, + media: query, + onchange: null, + addListener: vi.fn(), + removeListener: vi.fn(), + addEventListener: vi.fn(), + removeEventListener: vi.fn(), + dispatchEvent: vi.fn(), + }), +}); + +// Mock ResizeObserver +global.ResizeObserver = class ResizeObserver { + observe() {} + unobserve() {} + disconnect() {} +} as any; diff --git a/docs/next.config.js b/docs/next.config.js index 4019552d..36445448 100644 --- a/docs/next.config.js +++ b/docs/next.config.js @@ -12,7 +12,7 @@ const { i18n } = require('./next-i18next.config.cjs'); module.exports = withPlugins([], { transpilePackages: ['@kubed/components', '@kubed/hooks', '@kubed/hooks'], - reactStrictMode: false, + reactStrictMode: true, i18n, experimental: { forceSwcTransforms: true, diff --git a/docs/package.json b/docs/package.json index 8f4aa9a3..62ff9a06 100644 --- a/docs/package.json +++ b/docs/package.json @@ -9,9 +9,9 @@ "start": "next start" }, "dependencies": { - "@kubed/components": "^0.2.35", - "@kubed/hooks": "^0.2.35", - "@kubed/icons": "^0.2.35", + "@kubed/components": "workspace:*", + "@kubed/hooks": "workspace:*", + "@kubed/icons": "workspace:*", "@mdx-js/loader": "^3.0.1", "@mdx-js/react": "^1.6.22", "classnames": "^2.3.1", @@ -40,10 +40,10 @@ "@types/react": "^18", "@types/react-dom": "^18", "babel-plugin-styled-components": "^1.13.2", - "eslint": "^7.27.0", + "eslint": "^8.57.1", "eslint-config-next": "11.0.1", "next-compose-plugins": "^2.2.1", "next-mdx-remote": "^3.0.2", - "typescript": "^4.5.2" + "typescript": "5.8.3" } } diff --git a/docs/tsconfig.json b/docs/tsconfig.json index 218c572b..252cc4e1 100644 --- a/docs/tsconfig.json +++ b/docs/tsconfig.json @@ -2,6 +2,7 @@ "extends": "../tsconfig.json", // "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "../packages/**/*.tsx"], "compilerOptions": { + "baseUrl": "./", "paths": { "@docs/demos": [ "./docs/src/demos" @@ -30,5 +31,8 @@ "jsx": "preserve", "noImplicitAny": false, "incremental": true - } + }, + "exclude": [ + "node_modules" + ] } diff --git a/docusaurus/.gitignore b/docusaurus/.gitignore new file mode 100644 index 00000000..b2d6de30 --- /dev/null +++ b/docusaurus/.gitignore @@ -0,0 +1,20 @@ +# Dependencies +/node_modules + +# Production +/build + +# Generated files +.docusaurus +.cache-loader + +# Misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/docusaurus/README.md b/docusaurus/README.md new file mode 100644 index 00000000..01929c99 --- /dev/null +++ b/docusaurus/README.md @@ -0,0 +1,174 @@ +# Kube Design Documentation + +This is the official documentation website for Kube Design, built with [Docusaurus 3](https://docusaurus.io/). + +## Getting Started + +### Prerequisites + +- Node.js 16+ +- pnpm (recommended) + +### Installation + +Install dependencies from the monorepo root: + +```bash +cd /path/to/kube-design +pnpm install +``` + +### Development + +Start the development server: + +```bash +cd my-website +npm start +``` + +This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. + +The site will be available at `http://localhost:3000/kube-design/` + +### Build + +Build the website for production: + +```bash +npm run build +``` + +This command generates static content into the `build` directory. + +### Serve + +Test the production build locally: + +```bash +npm run serve +``` + +## Documentation Structure + +``` +my-website/ +├── docs/ # Documentation files +│ ├── intro.md # Homepage/Introduction +│ ├── guide/ # Guide section +│ │ ├── introduction.md +│ │ ├── installation.md +│ │ └── theme.md +│ ├── components/ # Component documentation +│ │ ├── button.mdx +│ │ ├── input.mdx +│ │ └── select.mdx +│ ├── hooks/ # Hooks documentation +│ │ ├── use-clipboard.mdx +│ │ └── use-toggle.mdx +│ └── icons/ # Icons documentation +│ └── overview.mdx +├── src/ +│ ├── components/ # React components for the site +│ └── pages/ # Custom pages +├── static/ # Static assets +├── docusaurus.config.ts # Site configuration +└── sidebars.ts # Sidebar navigation +``` + +## Features + +- ✅ **Multi-language Support**: English and Chinese +- ✅ **Component Documentation**: Comprehensive component API docs +- ✅ **Hooks Documentation**: React hooks usage guides +- ✅ **Icons Library**: Complete icon system documentation +- ✅ **Code Examples**: Interactive code examples in MDX +- ✅ **Dark Mode**: Built-in dark mode support +- ✅ **Search**: Full-text search functionality +- ✅ **Responsive**: Mobile-friendly design + +## Adding New Documentation + +### Add a New Component Doc + +1. Create a new MDX file in `docs/components/`: + ```bash + touch docs/components/my-component.mdx + ``` + +2. Add frontmatter and content: + ```mdx + --- + sidebar_position: 4 + --- + + # MyComponent + + Component description... + ``` + +3. Add to sidebar in `sidebars.ts`: + ```ts + componentsSidebar: [ + { + type: 'category', + label: 'Components', + items: [ + 'components/button', + 'components/input', + 'components/select', + 'components/my-component', // Add here + ], + }, + ], + ``` + +### Add a New Guide + +Follow the same pattern in `docs/guide/` and update `sidebars.ts`. + +## Configuration + +### Site Configuration + +Edit `docusaurus.config.ts` to configure: +- Site metadata (title, tagline, etc.) +- Deployment settings +- Theme settings +- Navbar items +- Footer links + +### Sidebar Configuration + +Edit `sidebars.ts` to organize documentation structure. + +## Deployment + +The site can be deployed to: +- GitHub Pages +- Netlify +- Vercel +- Any static hosting service + +For GitHub Pages: + +```bash +npm run build +# Deploy the build/ directory +``` + +## Contributing + +When adding new documentation: + +1. Follow the existing structure +2. Use MDX for component examples +3. Include proper frontmatter +4. Test locally before committing +5. Update sidebars.ts if needed + +## Learn More + +- [Docusaurus Documentation](https://docusaurus.io/) +- [Kube Design Repository](https://github.com/kubesphere/kube-design) +- [KubeSphere Console](https://github.com/kubesphere/console) diff --git a/docusaurus/algolia-crawler-config.json b/docusaurus/algolia-crawler-config.json new file mode 100644 index 00000000..86117319 --- /dev/null +++ b/docusaurus/algolia-crawler-config.json @@ -0,0 +1,78 @@ +{ + "index_name": "kube-design", + "start_urls": [ + { + "url": "https://kube-design-docusaurus.vercel.app/", + "selectors_key": "default" + }, + { + "url": "https://kube-design-docusaurus.vercel.app/docs/icons/", + "selectors_key": "icons", + "page_rank": 2 + } + ], + "sitemap_urls": ["https://kube-design-docusaurus.vercel.app/sitemap.xml"], + "stop_urls": ["/search", "\\.json$"], + "selectors": { + "default": { + "lvl0": { + "selector": "(//ul[contains(@class,'menu__list')]//a[contains(@class, 'menu__link--active')]/text() | //nav[contains(@class, 'navbar')]//a[contains(@class, 'navbar__link--active')]/text())[last()]", + "type": "xpath", + "global": true, + "default_value": "Documentation" + }, + "lvl1": "article h1, .theme-doc-markdown h1", + "lvl2": "article h2, .theme-doc-markdown h2", + "lvl3": "article h3, .theme-doc-markdown h3", + "lvl4": "article h4, .theme-doc-markdown h4", + "lvl5": "article h5, .theme-doc-markdown h5", + "lvl6": "article h6, .theme-doc-markdown h6", + "text": "article p, article li, .theme-doc-markdown p, .theme-doc-markdown li, article .table-of-contents__item" + }, + "icons": { + "lvl0": { + "selector": "article h1", + "global": true, + "default_value": "Icons" + }, + "lvl1": "article h2[id]:not([id^='category-'])", + "lvl2": { + "selector": "//h2[contains(@class, 'CategoryTitle')]/text()", + "type": "xpath", + "global": false + }, + "lvl3": { + "selector": "//div[contains(@class, 'IconCard')]//div[contains(@class, 'IconName')]", + "type": "xpath", + "global": false + }, + "text": "article p, article li" + } + }, + "selectors_exclude": [ + ".hash-link", + ".theme-code-block", + "nav.navbar", + "aside.theme-doc-sidebar-container", + ".theme-doc-toc-mobile", + ".theme-doc-toc-desktop", + ".theme-doc-breadcrumbs", + ".theme-edit-this-page", + ".theme-last-updated", + "footer.footer", + ".pagination-nav", + "input[type='text']", + ".StatsBar", + ".CopiedToast" + ], + "js_render": true, + "js_wait": 2, + "renderJavaScript": true, + "custom_settings": { + "separatorsToIndex": "_", + "attributesForFaceting": ["language", "version", "type", "docusaurus_tag"], + "attributesToRetrieve": ["hierarchy", "content", "anchor", "url", "url_without_anchor", "type"] + }, + "conversation_id": ["kube-design"], + "nb_hits": 46250 +} diff --git a/docusaurus/docs/components/action-confirm.mdx b/docusaurus/docs/components/action-confirm.mdx new file mode 100644 index 00000000..4ad74d6c --- /dev/null +++ b/docusaurus/docs/components/action-confirm.mdx @@ -0,0 +1,439 @@ +--- +sidebar_position: 1 +--- + +# ActionConfirm 操作确认栏 + +底部浮动的操作确认栏组件,用于确认或取消操作。 + +## 何时使用 + +- 需要用户确认表单提交或修改操作 +- 批量操作后需要确认 +- 需要在页面底部显示固定的操作按钮 +- 编辑场景下的保存/取消操作 + +## 示例 + +### 基础用法 + +ActionConfirm 需要放在一个相对定位的容器中,它会固定在容器底部。 + +```jsx live +function Demo() { + const [visible, setVisible] = React.useState(true); + + return ( +
+
+ +
+ alert('确认')} + onCancel={() => setVisible(false)} + /> +
+ ); +} +``` + +### 加载状态 + +通过 `confirmLoading` 属性显示确认按钮的加载状态,常用于异步操作。 + +```jsx live +function Demo() { + const [visible, setVisible] = React.useState(true); + const [loading, setLoading] = React.useState(false); + + const handleOk = () => { + setLoading(true); + setTimeout(() => { + setLoading(false); + setVisible(false); + }, 2000); + }; + + return ( +
+
+ +

+ 点击确认按钮后会显示加载状态 +

+
+ setVisible(false)} + /> +
+ ); +} +``` + +### 自定义按钮文本 + +通过 `okText` 和 `cancelText` 属性自定义按钮显示内容。 + +```jsx live +function Demo() { + const [visible, setVisible] = React.useState(true); + + return ( +
+
+ +
+ { + alert('已保存'); + setVisible(false); + }} + onCancel={() => setVisible(false)} + /> +
+ ); +} +``` + +### 表单编辑场景 + +在表单编辑时显示操作确认栏的典型用法。 + +```jsx live +function Demo() { + const [visible, setVisible] = React.useState(false); + const [loading, setLoading] = React.useState(false); + const [name, setName] = React.useState('nginx-deployment'); + + const handleChange = (e) => { + setName(e.target.value); + setVisible(true); + }; + + const handleSave = () => { + setLoading(true); + setTimeout(() => { + setLoading(false); + setVisible(false); + alert(`已保存: ${name}`); + }, 1000); + }; + + const handleCancel = () => { + setName('nginx-deployment'); + setVisible(false); + }; + + return ( +
+
+
编辑工作负载
+ +

+ 修改输入框内容后会显示确认栏 +

+
+ +
+ ); +} +``` + +### 配合卡片使用 + +在卡片组件中使用操作确认栏。 + +```jsx live +function Demo() { + const [visible, setVisible] = React.useState(false); + const [loading, setLoading] = React.useState(false); + + const handleSave = () => { + setLoading(true); + setTimeout(() => { + setLoading(false); + setVisible(false); + }, 1000); + }; + + return ( +
+ +
资源配置
+ + setVisible(true)} /> + setVisible(true)} /> + + setVisible(false)} + /> +
+
+ ); +} +``` + +### 删除确认 + +用于危险操作的确认场景。 + +```jsx live +function Demo() { + const [visible, setVisible] = React.useState(false); + const [loading, setLoading] = React.useState(false); + + const handleDelete = () => { + setLoading(true); + setTimeout(() => { + setLoading(false); + setVisible(false); + alert('已删除'); + }, 1000); + }; + + return ( +
+
+ +

+ 点击删除按钮后显示确认栏 +

+
+ setVisible(false)} + /> +
+ ); +} +``` + +## API + +### ActionConfirm + +| 属性 | 说明 | 类型 | 默认值 | +| -------------- | ---------------------- | ------------ | ----------------------------- | +| visible | 是否显示确认栏 | `boolean` | **必需** | +| okText | 确认按钮文本 | `ReactNode` | `` | +| cancelText | 取消按钮文本 | `ReactNode` | `` | +| confirmLoading | 确认按钮是否显示加载态 | `boolean` | `false` | +| onOk | 点击确认按钮的回调 | `() => void` | - | +| onCancel | 点击取消按钮的回调 | `() => void` | - | +| className | 自定义类名 | `string` | - | +| style | 自定义样式 | `CSSProperties` | - | + +:::info + +**关于定位**: + +- ActionConfirm 使用 `position: absolute` 定位,需要放在 `position: relative` 的容器中 +- 组件会固定在容器底部,通过 `bottom: 0; left: 0; right: 0;` 实现全宽布局 +- 距离容器边缘的间距为 12px(通过 `margin: 12px` 设置) +- 容器需要设置 `overflow: hidden` 以确保动画效果正常 +- z-index 为 999,确保在其他元素之上 + +**关于动画**: + +- 组件显示/隐藏时有平滑的过渡动画(transition: all 0.3s) +- 隐藏时的效果(ActionConfirm.tsx 第 21-25 行): + ```typescript + visibility: hidden; + opacity: 0; + transform: translateY(48px); // 向下滑出 48px + ``` +- 显示时的效果(ActionConfirm.tsx 第 13-18 行): + ```typescript + visibility: visible; + opacity: 1; + transform: translate(0); // 回到原位 + ``` + +**关于按钮样式**: + +- 按钮使用深色背景(`theme.palette.accents_8`),白色文字(`theme.palette.background`) +- 悬停时背景变为 `accents_7`(稍浅一些) +- 按钮内边距:`padding: 10px 20px` +- 按钮右边距:`margin-right: 12px` +- 按钮对齐方式:`justify-content: right` + +**关于加载状态**: + +- 当 `confirmLoading` 为 `true` 时,确认按钮会显示 Loading 组件(size: 14px) +- 加载状态下的样式(ActionConfirm.tsx 第 61-66 行): + ```typescript + background-color: accents_7; // 背景变浅 + cursor: not-allowed; // 禁用光标 + ``` +- 加载状态下点击确认按钮不会触发 `onOk` 回调(第 96 行判断 `!confirmLoading`) +- 适用于需要异步处理的场景 + +**关于图标**: + +- 默认确认按钮使用 `` 图标(浅色变体) +- 默认取消按钮使用 `` 图标(浅色变体) +- 可以通过 `okText` 和 `cancelText` 自定义为文本或其他内容 +- 图标来自 `@kubed/icons` 包 + +**关于点击处理**: + +- `onCancel` 点击处理:直接调用传入的回调函数(第 89-93 行) +- `onOk` 点击处理:只有在 `!confirmLoading` 时才调用回调(第 95-99 行) +- 这确保了加载状态下不会重复提交 + +::: + + +## 使用建议 + +### 容器设置 + +确保父容器正确设置定位和溢出属性: + +```jsx +// 正确:设置相对定位和隐藏溢出 +
+ +
+ +// 错误:没有设置定位 +
+ +
+``` + +### 表单场景 + +在表单修改时显示确认栏: + +```jsx +const [hasChanges, setHasChanges] = React.useState(false); + +const handleFieldChange = () => { + setHasChanges(true); +}; + +const handleSave = async () => { + await saveForm(); + setHasChanges(false); +}; + +const handleCancel = () => { + resetForm(); + setHasChanges(false); +}; + + +``` + +### 异步操作 + +处理异步保存操作: + +```jsx +const [loading, setLoading] = React.useState(false); + +const handleOk = async () => { + setLoading(true); + try { + await saveData(); + setVisible(false); + } catch (error) { + console.error(error); + } finally { + setLoading(false); + } +}; + + setVisible(false)} +/> +``` + +### 配合 Modal 使用 + +在 Modal 内部使用确认栏: + +```jsx + setModalVisible(false)}> +
+
{/* 表单内容 */}
+ +
+
+``` + +### 自定义按钮内容 + +按钮支持任意 ReactNode: + +```jsx +// 使用文本 + + +// 使用图标和文本 + + + 保存 + + } + cancelText={ + + + 取消 + + } +/> +``` diff --git a/docusaurus/docs/components/alert.mdx b/docusaurus/docs/components/alert.mdx new file mode 100644 index 00000000..94ba23c2 --- /dev/null +++ b/docusaurus/docs/components/alert.mdx @@ -0,0 +1,529 @@ +--- +sidebar_position: 1 +--- + +# Alert 警告提示 + +警告提示,用于向用户展示重要信息。 + +## 何时使用 + +- 需要向用户显示警告、错误或成功信息时 +- 页面顶部全局提示信息 +- 非浮层的静态展现形式,始终展现,不会自动消失 +- 可以配合可关闭按钮,用户可以手动关闭 + +## 示例 + +### 基础用法 + +最简单的用法,展示不同类型的提示信息。 + +```jsx live +function Demo() { + return ( + + 这是一条默认提示信息 + 这是一条信息提示 + 这是一条警告提示 + 这是一条错误提示 + + ); +} +``` + +### 带标题 + +通过 `title` 属性添加标题。 + +```jsx live +function Demo() { + return ( + + + 这是一条带标题的信息提示,可以包含更详细的说明内容。 + + + 这是一条带标题的警告提示,请注意查看相关内容。 + + + 这是一条带标题的错误提示,操作失败,请重试。 + + + ); +} +``` + +### 可关闭 + +通过 `closable` 属性添加关闭按钮。 + +```jsx live +function Demo() { + const [visible1, setVisible1] = React.useState(true); + const [visible2, setVisible2] = React.useState(true); + const [visible3, setVisible3] = React.useState(true); + + return ( + + {visible1 && ( + setVisible1(false)}> + 这是一条可关闭的信息提示 + + )} + {visible2 && ( + setVisible2(false)}> + 这是一条带标题的可关闭警告提示 + + )} + {visible3 && ( + setVisible3(false)}> + 这是一条可关闭的错误提示 + + )} + {!visible1 && !visible2 && !visible3 && ( + + )} + + ); +} +``` + +### 不显示图标 + +通过 `showIcon={false}` 隐藏图标。 + +```jsx live +function Demo() { + return ( + + + 不显示图标的信息提示 + + + 不显示图标的警告提示 + + + ); +} +``` + +### 自定义图标 + +通过 `icon` 属性自定义图标。 + +```jsx live +function Demo() { + const { Cluster, Storage, Network } = KubedIcons; + + return ( + + }> + 集群状态正常,所有节点运行正常 + + } title="存储警告"> + 存储空间不足,当前已使用 85% + + } title="网络错误"> + 网络连接失败,请检查网络配置 + + + ); +} +``` + +### 成功提示 + +使用 info 类型展示成功信息。 + +```jsx live +function Demo() { + return ( + + 操作成功完成 + + 集群 "my-cluster" 已成功创建,可以开始部署应用。 + + + ); +} +``` + +### 复杂内容 + +Alert 可以包含复杂的内容结构。 + +```jsx live +function Demo() { + return ( + + +
+

新版本 v2.0.0 已发布,包含以下更新:

+
    +
  • 新增集群监控功能
  • +
  • 优化部署流程
  • +
  • 修复已知问题
  • +
+ +
+
+ +
+

系统将于今晚 23:00 - 02:00 进行维护升级。

+

维护期间服务可能短暂中断,请提前做好准备。

+
+
+
+ ); +} +``` + +### 页面顶部全局提示 + +典型的页面顶部提示场景。 + +```jsx live +function Demo() { + const [showAlert, setShowAlert] = React.useState(true); + + return ( +
+ {showAlert && ( + setShowAlert(false)} + style={{ marginBottom: '16px' }} + > + 为了提供更好的服务,系统将于 2024 年 1 月 1 日进行升级维护,届时部分功能可能暂时不可用。 + + )} +
+

页面内容区域

+
+
+ ); +} +``` + +### 操作反馈 + +在操作后显示反馈信息。 + +```jsx live +function Demo() { + const [alerts, setAlerts] = React.useState([]); + + const addAlert = (type, message) => { + const id = Date.now(); + setAlerts([...alerts, { id, type, message }]); + }; + + const removeAlert = (id) => { + setAlerts(alerts.filter((alert) => alert.id !== id)); + }; + + return ( +
+ + + + + + + {alerts.map((alert) => ( + removeAlert(alert.id)}> + {alert.message} + + ))} + +
+ ); +} +``` + +### 表单验证提示 + +在表单中使用 Alert 显示验证结果。 + +```jsx live +function Demo() { + const [formData, setFormData] = React.useState({ username: '', password: '' }); + const [error, setError] = React.useState(''); + const [success, setSuccess] = React.useState(false); + + const handleSubmit = () => { + setError(''); + setSuccess(false); + + if (!formData.username || !formData.password) { + setError('请填写所有必填字段'); + return; + } + + if (formData.password.length < 6) { + setError('密码长度不能少于 6 个字符'); + return; + } + + setSuccess(true); + }; + + return ( +
+ {error && ( + setError('')} style={{ marginBottom: '16px' }}> + {error} + + )} + {success && ( + setSuccess(false)} + style={{ marginBottom: '16px' }} + > + 登录成功! + + )} + + setFormData({ ...formData, username: e.target.value })} + /> + setFormData({ ...formData, password: e.target.value })} + /> + + +
+ ); +} +``` + +## API + +### Alert 属性 + +| 属性 | 说明 | 类型 | 默认值 | +| -------- | ------------ | --------------------------------------------- | ----------- | +| type | 警告类型 | `'default' \| 'info' \| 'warning' \| 'error'` | `'default'` | +| title | 标题 | `ReactNode` | - | +| children | 提示内容 | `ReactNode` | - | +| icon | 自定义图标 | `ReactNode` | - | +| showIcon | 是否显示图标 | `boolean` | `true` | +| closable | 是否可关闭 | `boolean` | `false` | +| onClose | 关闭时的回调 | `() => void` | - | +| 其他 | 原生属性 | `HTMLAttributes` | - | + +:::info + +**关于类型**: + +- `default`:默认提示,使用灰色主题 +- `info`:信息提示,使用蓝色主题,常用于成功消息 +- `warning`:警告提示,使用黄色主题 +- `error`:错误提示,使用红色主题 + +**关于图标**: + +- 默认情况下会根据 `type` 显示对应的图标 +- 通过 `icon` 属性可以自定义图标 +- 通过 `showIcon={false}` 可以隐藏图标 +- 带标题时图标会变大 + +**关于关闭**: + +- `closable` 属性控制是否显示关闭按钮 +- 点击关闭按钮会触发 `onClose` 回调 +- 组件本身不处理关闭逻辑,需要配合状态管理 + +Alert 组件继承所有原生 HTML div 元素的属性。 + +::: + +## 使用建议 + +### 选择合适的类型 + +根据消息的性质选择合适的类型: + +```jsx +// 成功消息 - 使用 info 类型(带绿色对勾图标) + + 操作成功完成 + + +// 一般信息 - 使用 default 类型 + + 这是一条普通信息 + + +// 警告信息 - 使用 warning 类型 + + 配置可能导致问题,请仔细检查 + + +// 错误信息 - 使用 error 类型 + + 操作失败,请重试 + +``` + +### 可关闭的提示 + +实现可关闭提示的推荐方式: + +```jsx +const [visible, setVisible] = React.useState(true); + +{ + visible && ( + setVisible(false)}> + 这是一条可关闭的提示 + + ); +} + +// 重置按钮(可选) +{ + !visible && ; +} +``` + +### Alert vs Notification + +选择使用 Alert 还是 notification: + +```jsx +// Alert:静态展示,不会自动消失 +这条消息会一直显示,直到用户关闭; + +// Notification:浮层提示,自动消失 +notification.success({ + title: '操作成功', + content: '这条消息会在几秒后自动消失', + duration: 3000, +}); +``` + +### 页面级提示 + +在页面顶部显示全局提示: + +```jsx +function Page() { + const [showNotice, setShowNotice] = React.useState(true); + + return ( +
+ {showNotice && ( + setShowNotice(false)} + style={{ marginBottom: '20px' }} + > + 系统维护通知内容... + + )} + {/* 页面内容 */} +
+ ); +} +``` + +### 表单验证 + +在表单中使用 Alert: + +```jsx +const [errors, setErrors] = React.useState([]); + +const validate = () => { + const newErrors = []; + if (!formData.name) newErrors.push('请输入名称'); + if (!formData.email) newErrors.push('请输入邮箱'); + setErrors(newErrors); + return newErrors.length === 0; +}; + +{ + errors.length > 0 && ( + +
    + {errors.map((error, index) => ( +
  • {error}
  • + ))} +
+
+ ); +} +``` + +### 动态提示列表 + +管理多个动态提示: + +```jsx +const [alerts, setAlerts] = React.useState([]); + +const addAlert = (type, message) => { + const id = Date.now(); + setAlerts([...alerts, { id, type, message }]); +}; + +const removeAlert = (id) => { + setAlerts(alerts.filter((alert) => alert.id !== id)); +}; + +<> + {alerts.map((alert) => ( + removeAlert(alert.id)}> + {alert.message} + + ))} +; +``` + +### 内容排版 + +对于包含复杂内容的 Alert: + +```jsx + +
+

段落文本内容...

+
    +
  • 列表项 1
  • +
  • 列表项 2
  • +
+ +
+
+``` diff --git a/docusaurus/docs/components/autocomplete.mdx b/docusaurus/docs/components/autocomplete.mdx new file mode 100644 index 00000000..461c73dd --- /dev/null +++ b/docusaurus/docs/components/autocomplete.mdx @@ -0,0 +1,391 @@ +--- +sidebar_position: 1 +--- + +# AutoComplete 自动完成 + +输入框自动完成功能,提供输入建议。 + +## 何时使用 + +- 需要根据用户输入提供相关建议 +- 需要快速搜索和选择数据 +- 输入框需要智能提示功能 + +## 示例 + +### 基础用法 + +基本使用,通过 `options` 设置自动完成的数据源。 + +```jsx live +function Demo() { + const [value, setValue] = React.useState(''); + const [options, setOptions] = React.useState([]); + + const handleSearch = (searchText) => { + if (!searchText) { + setOptions([]); + } else { + setOptions([ + { value: searchText }, + { value: searchText + searchText }, + { value: searchText + searchText + searchText }, + ]); + } + }; + + const handleChange = (data) => { + setValue(data); + }; + + return ( + + ); +} +``` + +### 自定义选项 + +通过 `options` 配置不同的选项内容。 + +```jsx live +function Demo() { + const [value, setValue] = React.useState(''); + const [options, setOptions] = React.useState([]); + + const searchResult = (query) => { + const results = [ + { value: `${query}@gmail.com`, text: `${query}@gmail.com` }, + { value: `${query}@163.com`, text: `${query}@163.com` }, + { value: `${query}@qq.com`, text: `${query}@qq.com` }, + { value: `${query}@outlook.com`, text: `${query}@outlook.com` }, + ]; + return results; + }; + + const handleSearch = (searchText) => { + setOptions(!searchText ? [] : searchResult(searchText)); + }; + + return ( + + ); +} +``` + +### 查询模式 + +通过 `onSearch` 实现远程搜索功能。 + +```jsx live +function Demo() { + const [value, setValue] = React.useState(''); + const [options, setOptions] = React.useState([]); + const [loading, setLoading] = React.useState(false); + + const mockSearch = (query) => { + return new Promise((resolve) => { + setTimeout(() => { + const data = ['Kubernetes', 'KubeSphere', 'Docker', 'Helm', 'Istio', 'Prometheus'].filter( + (item) => item.toLowerCase().includes(query.toLowerCase()) + ); + resolve(data.map((item) => ({ value: item }))); + }, 300); + }); + }; + + const handleSearch = async (searchText) => { + if (!searchText) { + setOptions([]); + return; + } + + setLoading(true); + const results = await mockSearch(searchText); + setOptions(results); + setLoading(false); + }; + + return ( + + ); +} +``` + +### 选中回调 + +使用 `onSelect` 获取选中的值。 + +```jsx live +function Demo() { + const [value, setValue] = React.useState(''); + const [options, setOptions] = React.useState([]); + const [selected, setSelected] = React.useState(''); + + const handleSearch = (searchText) => { + if (!searchText) { + setOptions([]); + } else { + setOptions( + [ + { value: 'Kubernetes', text: 'Kubernetes - 容器编排' }, + { value: 'Docker', text: 'Docker - 容器引擎' }, + { value: 'Helm', text: 'Helm - 包管理器' }, + ].filter((item) => item.value.toLowerCase().includes(searchText.toLowerCase())) + ); + } + }; + + const handleSelect = (data) => { + setSelected(`您选择了: ${data}`); + }; + + return ( +
+ + {selected &&
{selected}
} +
+ ); +} +``` + +### 不区分大小写 + +实现不区分大小写的搜索功能。 + +```jsx live +function Demo() { + const [value, setValue] = React.useState(''); + const [options, setOptions] = React.useState([]); + + const allOptions = ['JavaScript', 'TypeScript', 'Python', 'Java', 'Golang', 'Rust', 'C++', 'PHP']; + + const handleSearch = (searchText) => { + if (!searchText) { + setOptions([]); + } else { + const filtered = allOptions.filter((item) => + item.toLowerCase().includes(searchText.toLowerCase()) + ); + setOptions(filtered.map((item) => ({ value: item }))); + } + }; + + return ( + + ); +} +``` + +### 自定义输入框 + +可以自定义输入框样式。 + +```jsx live +function Demo() { + const [value, setValue] = React.useState(''); + const [options, setOptions] = React.useState([]); + + const handleSearch = (searchText) => { + if (!searchText) { + setOptions([]); + } else { + setOptions([ + { value: `查找 "${searchText}"` }, + { value: `搜索 "${searchText}" 相关内容` }, + { value: `在文档中查找 "${searchText}"` }, + ]); + } + }; + + return ( + + ); +} +``` + +### 禁用状态 + +禁用 AutoComplete。 + +```jsx live +function Demo() { + return ( + + + + + ); +} +``` + +### 带清除按钮 + +设置 `allowClear` 显示清除按钮。 + +```jsx live +function Demo() { + const [value, setValue] = React.useState(''); + const [options, setOptions] = React.useState([]); + + const handleSearch = (searchText) => { + if (!searchText) { + setOptions([]); + } else { + setOptions([{ value: 'Option 1' }, { value: 'Option 2' }, { value: 'Option 3' }]); + } + }; + + return ( + + ); +} +``` + +## API + +### AutoComplete 属性 + +| 属性 | 说明 | 类型 | 默认值 | +| ------------ | -------------------- | ----------------------------------------- | ------- | +| value | 输入框的值 | `string` | - | +| defaultValue | 默认值 | `string` | - | +| options | 数据源 | `Array<{ value: string; text?: string }>` | `[]` | +| placeholder | 输入框占位符 | `string` | - | +| disabled | 是否禁用 | `boolean` | `false` | +| allowClear | 是否显示清除按钮 | `boolean` | `false` | +| onSearch | 搜索补全项时的回调 | `(value: string) => void` | - | +| onChange | 输入框值变化时的回调 | `(value: string) => void` | - | +| onSelect | 选中选项时的回调 | `(value: string) => void` | - | +| onFocus | 获得焦点时的回调 | `() => void` | - | +| onBlur | 失去焦点时的回调 | `() => void` | - | +| 其他 | 原生属性 | `React.HTMLAttributes` | - | + +:::info + +**options 数据格式**: + +- 简单格式:`[{ value: 'text' }]` +- 完整格式:`[{ value: 'value', text: 'display text' }]` +- 字符串数组会自动转换为 `{ value: string }` 格式 + +**搜索逻辑**: + +- `onSearch` 在输入框值变化时触发 +- 通过 `onSearch` 更新 `options` 来显示建议列表 +- 建议在 `onSearch` 中实现防抖逻辑以优化性能 + +AutoComplete 组件继承 Select 组件的大部分属性和方法。 + +::: + +## 使用建议 + +### 性能优化 + +对于大量数据或远程搜索,建议实现防抖: + +```jsx +import { useDebouncedValue } from '@kubed/hooks'; + +function Demo() { + const [value, setValue] = React.useState(''); + const [debouncedValue] = useDebouncedValue(value, 300); + + React.useEffect(() => { + if (debouncedValue) { + // 执行搜索 + } + }, [debouncedValue]); + + return ; +} +``` + +### 数据过滤 + +推荐的过滤逻辑: + +```jsx +const filterOptions = (searchText, allOptions) => { + const query = searchText.toLowerCase().trim(); + if (!query) return []; + + return allOptions.filter((option) => option.value.toLowerCase().includes(query)).slice(0, 10); // 限制结果数量 +}; +``` + +### 远程搜索 + +实现远程搜索时的最佳实践: + +```jsx +const handleSearch = async (searchText) => { + if (!searchText || searchText.length < 2) { + setOptions([]); + return; + } + + setLoading(true); + try { + const results = await fetchSuggestions(searchText); + setOptions(results); + } catch (error) { + console.error('Search failed:', error); + } finally { + setLoading(false); + } +}; +``` diff --git a/docusaurus/docs/components/badge.mdx b/docusaurus/docs/components/badge.mdx new file mode 100644 index 00000000..e526e269 --- /dev/null +++ b/docusaurus/docs/components/badge.mdx @@ -0,0 +1,379 @@ +--- +sidebar_position: 1 +--- + +# Badge 徽标 + +用于展示状态标记或计数的徽标组件。 + +## 何时使用 + +- 需要在元素上展示状态或数量信息 +- 用于头像、图标或其他元素的角标提示 +- 展示未读消息数、在线状态等 +- 需要突出显示某些状态信息 + +## 示例 + +### 基础用法 + +最简单的用法,展示数字或文本。 + +```jsx live +function Demo() { + return ( + + 3 + 99+ + New + + ); +} +``` + +### 颜色 + +通过 `color` 属性设置徽标颜色,支持主题预设色和自定义颜色。 + +```jsx live +function Demo() { + return ( + + Default + Primary + Secondary + Success + Warning + Error + + ); +} +``` + +### 自定义颜色 + +支持使用 HEX、RGB、RGBA 颜色值。 + +```jsx live +function Demo() { + return ( + + Blue + Green + Purple + Red + Yellow + + ); +} +``` + +### 阴影效果 + +通过 `shadow` 属性添加阴影效果,使徽标更加突出。 + +```jsx live +function Demo() { + return ( + + + Primary + + + Success + + + Warning + + + Error + + + ); +} +``` + +### 点状徽标 + +通过 `dot` 属性展示为点状徽标,常用于状态指示。 + +```jsx live +function Demo() { + return ( + + + + + + ); +} +``` + +### 动画效果 + +通过 `motion` 属性为点状徽标添加动画效果,适用于需要吸引注意力的场景。 + +```jsx live +function Demo() { + return ( + + + + + + ); +} +``` + +### 锚点定位 + +使用 `BadgeAnchor` 组件将徽标定位在其他元素上。 + +```jsx live +function Demo() { + const { Storage, Pod, Cluster } = KubedIcons; + + return ( + + + 5 + + + + 12 + + + + 99+ + + + + ); +} +``` + +### 锚点位置 + +通过 `placement` 属性设置徽标在锚点元素上的位置。 + +```jsx live +function Demo() { + const { Storage } = KubedIcons; + + return ( + + + 1 + + + + 2 + + + + 3 + + + + 4 + + + + ); +} +``` + +### 偏移量 + +通过 `offset` 属性调整徽标的位置偏移。 + +```jsx live +function Demo() { + const { Pod } = KubedIcons; + + return ( + + + + + + + + + + + + + + + ); +} +``` + +### 配合 Tooltip 使用 + +结合 Tooltip 展示更多信息。 + +```jsx live +function Demo() { + const { Storage, Pod } = KubedIcons; + + return ( + + + + 5 + + + + + + + + + + + ); +} +``` + +### 实际应用场景 + +展示一些常见的使用场景。 + +```jsx live +function Demo() { + const { Bell, Mail, Cart } = KubedIcons; + + return ( + + + 3 + + + + 12 + + + + + + + + ); +} +``` + +## API + +### Badge + +| 属性 | 说明 | 类型 | 默认值 | +| -------- | ---------------------------- | -------------------------------------------------------------------------------------- | ------- | +| color | 徽标颜色 | `'default' \| 'primary' \| 'secondary' \| 'success' \| 'warning' \| 'error' \| string` | - | +| shadow | 是否显示阴影 | `boolean` | `false` | +| dot | 是否显示为点状 | `boolean` | `false` | +| motion | 是否显示动画(仅点状时有效) | `boolean` | `false` | +| children | 徽标内容 | `ReactNode` | - | + +### BadgeAnchor + +| 属性 | 说明 | 类型 | 默认值 | +| --------- | ---------- | ---------------------------------------------------------- | --------------- | +| placement | 徽标位置 | `'topLeft' \| 'topRight' \| 'bottomLeft' \| 'bottomRight'` | `'bottomRight'` | +| offset | 位置偏移量 | `[number, number]` | `[0, 0]` | +| children | 子元素 | `ReactNode` | - | + +:::info + +**关于颜色**: + +- 支持主题预设颜色:`primary`、`secondary`、`success`、`warning`、`error` +- 支持 HEX 格式:`#ff6b6b`、`#228be6` +- 支持 RGB/RGBA 格式:`rgb(255, 107, 107)`、`rgba(255, 107, 107, 0.8)` +- 不传 `color` 时使用默认灰色样式 + +**关于点状徽标**: + +- 当 `dot` 为 `true` 时,Badge 会渲染为 StatusDot 组件 +- `motion` 属性只在 `dot` 为 `true` 时生效 +- 点状徽标常用于状态指示,如在线/离线状态 + +**关于 BadgeAnchor**: + +- BadgeAnchor 是一个定位容器,第一个子元素应该是 Badge +- 第二个子元素是要添加徽标的目标元素 +- `offset` 数组格式为 `[x, y]`,用于微调位置 + +::: + +## 使用建议 + +### 数字展示 + +对于较大的数字,建议使用截断显示: + +```jsx +// 推荐:使用截断显示 +{count > 99 ? '99+' : count} + +// 不推荐:显示过长的数字 +12345 +``` + +### 状态指示 + +使用点状徽标表示状态: + +```jsx +// 在线状态 + + + + + +// 离线状态 + + + + + +// 忙碌状态 + + + + +``` + +### 配合图标使用 + +在图标按钮上显示计数: + +```jsx + + {unreadCount} + + + + +``` + +### 阴影使用 + +在需要更强调或在复杂背景上时使用阴影: + +```jsx +// 需要强调时 +Important + +// 在深色背景上 +
+ Attention +
+``` diff --git a/docusaurus/docs/components/banner.mdx b/docusaurus/docs/components/banner.mdx new file mode 100644 index 00000000..e8676872 --- /dev/null +++ b/docusaurus/docs/components/banner.mdx @@ -0,0 +1,526 @@ +--- +sidebar_position: 1 +--- + +# Banner 横幅 + +用于在页面顶部展示重要信息的横幅组件。 + +## 何时使用 + +- 在页面顶部展示页面标题和说明信息 +- 需要向用户展示相关提示或帮助信息 +- 在详情页面或列表页面提供上下文信息 + +在 Kube Design 中,Banner 组件提供了丰富的横幅功能: + +- **图标支持**:可以添加图标增强视觉效果 +- **标题和描述**:展示页面标题和说明文字 +- **导航集成**:可以嵌入导航标签 +- **提示信息**:支持可折叠的 BannerTip 提示 + +## 示例 + +### 基础用法 + +最基本的横幅用法,包含图标、标题和描述。 + +```jsx live +function Demo() { + const { Cluster } = KubedIcons; + + return ( + } + title="集群节点" + description="集群节点提供了当前集群下节点的运行状态,以及可以编辑删除节点" + /> + ); +} +``` + +### 不同图标 + +使用不同图标展示不同类型的页面。 + +```jsx live +function Demo() { + const { Pod, Service, ConfigMap, Project } = KubedIcons; + + return ( + + } + title="容器组" + description="容器组 (Pod) 是 Kubernetes 应用程序的基本执行单元" + /> + } + title="服务" + description="服务 (Service) 定义了访问一组 Pod 的方式" + /> + } + title="配置字典" + description="配置字典 (ConfigMap) 用于保存非机密性的配置数据" + /> + + ); +} +``` + +### 带导航 + +在横幅中嵌入导航标签。 + +```jsx live +function Demo() { + const { Cluster } = KubedIcons; + + const navData = [ + { label: 'KubeSphere', value: 'kubesphere' }, + { label: 'Kubernetes', value: 'kubernetes' }, + { label: 'Jenkins', value: 'jenkins' }, + ]; + + return ( + } + title="集群管理" + description="管理和监控您的 Kubernetes 集群" + > + + + ); +} +``` + +### 带提示信息 + +使用 BannerTip 添加可折叠的提示信息。 + +```jsx live +function Demo() { + const { Cluster } = KubedIcons; + + return ( + } + title="集群节点" + description="集群节点提供了当前集群下节点的运行状态" + > + + 集群节点是 Kubernetes 集群中的工作机器,可以是物理机或虚拟机。每个节点都由控制平面管理,包含运行 Pod 所需的服务。 + + + 节点分为主控 (Master) 节点和工作 (Worker) 节点。Master 节点负责管理集群状态,Worker 节点负责运行应用负载。 + + + ); +} +``` + +### 不可移除的提示 + +设置 `removable={false}` 禁止移除提示。 + +```jsx live +function Demo() { + const { Information } = KubedIcons; + + return ( + } + title="系统公告" + description="重要的系统通知和公告" + > + + 系统将于本周六凌晨 2:00-4:00 进行维护升级,届时服务将暂时不可用。请提前做好准备。 + + + ); +} +``` + +### 带操作按钮的提示 + +在提示中添加操作按钮。 + +```jsx live +function Demo() { + const { Cluster } = KubedIcons; + + return ( + } + title="集群节点" + description="集群节点提供了当前集群下节点的运行状态" + > + 查看文档} + > + 您可以通过添加节点功能将新的机器加入到集群中,扩展集群的计算能力。 + + 了解更多} + > + 节点污点 (Taints) 可以阻止某些容器组副本部署至该节点中,与容忍度 (Tolerations) 一起工作确保容器组不会被调度到不合适的节点上。 + + + ); +} +``` + +### 导航和提示结合 + +同时包含导航和提示信息。 + +```jsx live +function Demo() { + const { Project } = KubedIcons; + + const navData = [ + { label: '全部项目', value: 'all' }, + { label: '我的项目', value: 'mine' }, + { label: '已归档', value: 'archived' }, + ]; + + return ( + } + title="项目管理" + description="项目用于对资源进行分组管理,可以按项目对资源进行隔离" + > + + + 项目 (Project) 是 KubeSphere 的基本资源隔离单元,对应 Kubernetes 的 Namespace。您可以在项目中创建工作负载、服务等资源。 + + + 点击右上角的"创建"按钮,填写项目名称、描述等信息即可创建新项目。创建后可以设置资源配额和网络策略。 + + + ); +} +``` + +### 多个提示 + +展示多个提示信息。 + +```jsx live +function Demo() { + const { Pod } = KubedIcons; + + return ( + } + title="容器组" + description="容器组是 Kubernetes 中最小的可部署单元" + > + + 容器组 (Pod) 是一组共享存储和网络资源的容器集合。它是 Kubernetes 调度的最小单位。 + + + 容器组有多种状态:Pending、Running、Succeeded、Failed、Unknown。了解这些状态有助于排查问题。 + + + 选择容器组,点击"日志"按钮即可查看容器的运行日志。也可以使用 kubectl logs 命令查看。 + + + 选择容器组,点击"终端"按钮可以进入容器的命令行终端,进行调试和排查问题。 + + + ); +} +``` + +### 应用部署场景 + +在应用部署页面的使用示例。 + +```jsx live +function Demo() { + const { Application } = KubedIcons; + + const navData = [ + { label: '应用模板', value: 'template' }, + { label: '自制应用', value: 'custom' }, + { label: '应用仓库', value: 'repo' }, + ]; + + return ( + } + title="应用管理" + description="使用应用模板或 Helm Chart 部署和管理应用" + > + + + 应用模板基于 Helm Chart 打包,包含了应用的所有 Kubernetes 资源定义和配置。您可以一键部署完整的应用栈。 + + 开始创建} + > + 自制应用允许您从零开始构建应用,可以添加多个服务组件,配置路由和存储。 + + + ); +} +``` + +### 自定义样式 + +通过 className 自定义样式。 + +```jsx live +function Demo() { + const { Setting } = KubedIcons; + + return ( + } + title="系统设置" + description="配置系统全局参数和偏好设置" + style={{ backgroundColor: '#f5f7fa' }} + > + + 在系统设置中选择"语言"选项,选择您需要的语言即可。系统支持中文、英文等多种语言。 + + + ); +} +``` + +## API + +### Banner + +| 属性 | 说明 | 类型 | 默认值 | +| ----------- | -------------- | --------------- | ------ | +| icon | 横幅图标 | `ReactNode` | - | +| title | 横幅标题 | `ReactNode` | - | +| description | 横幅描述 | `ReactNode` | - | +| children | 子元素(Navs 或 BannerTip) | `ReactNode` | - | +| className | 自定义类名 | `string` | - | +| style | 自定义样式 | `CSSProperties` | - | + +### BannerTip + +| 属性 | 说明 | 类型 | 默认值 | +| ---------- | ------------------ | ------------------------ | ------- | +| key | 提示的唯一标识 | `string` | **必需**| +| title | 提示标题 | `ReactNode` | - | +| children | 提示内容 | `ReactNode` | - | +| removable | 是否可以移除 | `boolean` | `true` | +| operations | 操作按钮 | `ReactNode` | - | + +:::info + +**关于 Banner 组成**: + +- Banner 基于 Card 组件构建(Banner.tsx 第 98 行),设置 `padding={0}` +- Banner 由三部分组成: + - BannerTitle: 包含图标、标题和描述(第 99-107 行) + - BannerNavs: 导航区域,背景色为 `accents_1`(第 109 行) + - BannerExtra: 提示区域,包含所有 BannerTip(第 108-128 行) +- 额外内容(children)会自动分类:BannerTip 和其他元素(如 Navs) +- BannerTip 会自动排列在导航下方 + +**关于图标区域**: + +- 图标容器尺寸固定为 60x60px(BannerIcon: 第 23-24 行) +- 图标本身尺寸为 48x48px(第 27-30 行) +- 特殊的圆角设计:`border-radius: 100px 0 100px 100px`(第 19 行,右上角为直角) +- 背景色为 `accents_1`,内边距 6px +- 与标题区域间距 12px(margin-right) + +**关于标题样式**: + +- 标题使用 Text 组件,variant="h3",size=24,color="accents_8"(第 102-103 行) +- 标题有文字阴影效果:`text-shadow: 0 4px 8px rgba(accents_8, 0.1)`(第 38 行) +- 描述文字颜色为 `accents_5`(第 105 行) +- 标题区域内边距:`padding: 24px 20px 21px`(第 14 行) + +**关于 BannerTip**: + +- BannerTip 必须设置 `key` 属性作为唯一标识(第 62 行) +- 点击 BannerTip 会展开/折叠内容(通过 onClick 回调) +- 展开时显示向下箭头 ChevronDown,折叠时显示向右箭头 ChevronRight(BannerTip.tsx 第 97 行) +- 默认可以通过关闭按钮移除,设置 `removable={false}` 禁用(第 85 行默认值为 true) +- 可以通过 `operations` 添加自定义操作按钮,显示在右侧(第 102-104 行) + +**关于状态管理**: + +- Banner 使用 useState 管理活动的提示 key(Banner.tsx 第 68 行) +- 同一时间只能有一个 BannerTip 处于展开状态(第 70-76 行逻辑) +- 移除的 BannerTip 会被记录在 removedTipKeys 数组中(第 69、78-81 行) +- 移除的提示通过 `removedTipKeys.indexOf(child.key) < 0` 过滤(第 89 行) +- 状态在组件生命周期内持久化,刷新页面后重置 + +**关于子元素处理**: + +- Banner 会遍历 children,将 BannerTip 类型的子元素单独提取(第 86-95 行) +- 通过 `child.type === BannerTip` 判断是否为 BannerTip 组件(第 88 行) +- 其他类型的子元素(如 Navs)放入 others 数组,渲染在 BannerNavs 中(第 109 行) +- BannerTip 通过 map 渲染,传入 open、onClick、onRemove 等属性(第 110-127 行) + +**关于 BannerTip 样式**: + +- BannerTip 内边距:`padding: 12px 60px 12px 52px`(BannerTip.tsx 第 9 行) +- 上边框:`border-top: 1px solid accents_2`(第 11 行) +- hover 效果:背景变为 `accents_0`(第 15-17 行) +- 图标位置:绝对定位 `left: 24px, top: 11px`(第 21-23 行) +- 操作区域:绝对定位在右侧,垂直居中(第 36-42 行) +- 关闭图标大小:20px(第 104 行) + +**关于展开动画**: + +- 展开时直接显示内容,无额外动画 +- 内容通过条件渲染:`{open && ...}`(BannerTip.tsx 第 100 行) +- 图标切换有 hover 效果,背景色变化(第 44-47 行) + +**关于移除操作**: + +- 点击关闭图标会触发 onRemove 回调,传入 tipKey(第 90-93 行) +- 使用 stopPropagation 阻止事件冒泡,避免触发 onClick(第 91 行) +- Banner 收到移除通知后更新 removedTipKeys 状态(Banner.tsx 第 78-81 行) +- 移除后的 BannerTip 在重新渲染时被过滤掉 + +::: + + +## 使用建议 + +### 保持描述简洁 + +横幅描述应该简洁明了: + +```jsx +// 推荐: 简洁描述 + + +// 不推荐: 描述过长 + +``` + +### 选择合适的图标 + +使用与内容相关的图标: + +```jsx +import { Pod, Service, Cluster, Setting } from '@kubed/icons'; + +// 容器组页面 +} title="容器组" /> + +// 服务页面 +} title="服务" /> + +// 集群页面 +} title="集群" /> +``` + +### 提示信息不宜过多 + +保持提示数量在合理范围: + +```jsx +// 推荐: 2-4 个提示 + + ... + ... + ... + + +// 不推荐: 提示过多,考虑使用文档链接 + + {/* 5+ 个提示 */} + +``` + +### 重要提示设为不可移除 + +对于重要的系统通知,设置不可移除: + +```jsx + + 系统维护通知内容... + +``` + +### 添加操作按钮 + +为用户提供快速操作入口: + +```jsx +查看教程} +> + 点击查看教程了解更多... + +``` + +### 配合导航使用 + +在需要分类展示时添加导航: + +```jsx + + + ... + +``` + +### 确保 key 唯一 + +每个 BannerTip 必须有唯一的 key: + +```jsx + + 提示 1 + 提示 2 + 提示 3 + +``` + +### 页面级使用 + +Banner 通常用于页面顶部: + +```jsx +function PageComponent() { + return ( +
+ } + title="页面标题" + description="页面描述" + > + 帮助信息 + +
+ {/* 页面内容 */} +
+
+ ); +} +``` diff --git a/docusaurus/docs/components/button.mdx b/docusaurus/docs/components/button.mdx new file mode 100644 index 00000000..07b130ca --- /dev/null +++ b/docusaurus/docs/components/button.mdx @@ -0,0 +1,271 @@ +--- +sidebar_position: 1 +--- + +# Button + +用于触发一个操作。 + +## 何时使用 + +按钮代表一个操作(或一系列操作)。点击按钮将触发相应的业务逻辑。 + +在 Kube Design 中我们提供了 4 种按钮类型: + +- **主要按钮**:表示主要操作,一个区域中最多只有一个主要按钮 +- **默认按钮**:表示一系列没有优先级的操作 +- **文本按钮**:用于最次要的操作 +- **链接按钮**:用于外部链接 + +## 示例 + +### 变体 + +Kube Design 中提供了填充按钮、轮廓按钮、文本按钮和链接按钮。 + +```jsx live +function Demo() { + return ( + + + + + + + ); +} +``` + +### 颜色 + +Button 有六种颜色。color 属性可以设置为 `default`、`primary`、`secondary`、`success`、`warning` 和 `error`。 + +```jsx live +function Demo() { + return ( + + + + + + + + + ); +} +``` + +### 禁用状态 + +要将按钮标记为禁用状态,请向 Button 添加 `disabled` 属性。 + +```jsx live +function Demo() { + return ( + + + + + + ); +} +``` + +### 块级按钮 + +block 属性将使按钮适应其父元素的宽度。 + +```jsx live +function Demo() { + return ( +
+ +
+ +
+
+ ); +} +``` + +### 阴影按钮 + +shadow 属性将使按钮具有阴影效果。 + +```jsx live +function Demo() { + return ( + + + + + + + + ); +} +``` + +### 尺寸和圆角 + +使用 `size` 和 `radius` 属性更改按钮的尺寸或圆角。您可以将值设置为 `xs`、`sm`、`md`、`lg` 或 `xl`。 + +```jsx live +function Demo() { + return ( + + + + + + + + ); +} +``` + +### 加载状态 + +通过设置 `loading` 属性可以向按钮添加加载指示器。 + +```jsx live +function Demo() { + const [loading, setLoading] = React.useState(false); + + const handleClick = () => { + setLoading(true); + setTimeout(() => setLoading(false), 2000); + }; + + return ( + + + + + ); +} +``` + +### 带图标 + +Button 组件可以包含图标。这可以通过设置 `leftIcon` 或 `rightIcon` 属性,或在 Button 中放置 Icon 组件来实现。 + +```jsx live +function Demo() { + const { Add } = KubedIcons; + return ( + + + + + + + ); +} +``` + +### 作为不同元素 + +`as` 属性可以更改组件使用的底层元素。 + +```jsx live +function Demo() { + return ( + + + + + ); +} +``` + +## API + +### Button 属性 + +| 属性 | 说明 | 类型 | 默认值 | +| --------- | ------------ | ---------------------------------------------------------------------------- | ----------- | +| variant | 按钮变体 | `'filled' \| 'outline' \| 'text' \| 'link'` | `'filled'` | +| color | 按钮颜色 | `'default' \| 'primary' \| 'secondary' \| 'success' \| 'warning' \| 'error'` | `'default'` | +| size | 按钮尺寸 | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl'` | `'sm'` | +| radius | 边框圆角 | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| number` | `'xl'` | +| disabled | 禁用状态 | `boolean` | `false` | +| loading | 加载状态 | `boolean` | `false` | +| block | 全宽按钮 | `boolean` | `false` | +| shadow | 添加阴影效果 | `boolean` | `false` | +| leftIcon | 左侧图标 | `ReactNode` | - | +| rightIcon | 右侧图标 | `ReactNode` | - | +| as | 更改底层元素 | `string \| Component` | `'button'` | +| className | 自定义类名 | `string` | - | +| children | 按钮内容 | `ReactNode` | - | +| 其他 | 原生属性 | `ButtonHTMLAttributes` | - | + +:::info +Button 组件继承所有原生 HTML button 元素的属性(如 `onClick`、`onMouseEnter`、`type` 等)。当使用 `as` 属性时,会继承对应元素的属性。 +::: diff --git a/docusaurus/docs/components/card.mdx b/docusaurus/docs/components/card.mdx new file mode 100644 index 00000000..f3f6d978 --- /dev/null +++ b/docusaurus/docs/components/card.mdx @@ -0,0 +1,361 @@ +--- +sidebar_position: 1 +--- + +# Card 卡片 + +通用的内容容器组件,用于展示信息和内容分组。 + +## 何时使用 + +- 需要将相关内容组织在一起展示 +- 需要一个带有阴影效果的容器来突出内容 +- 展示概览信息、统计数据等 +- 作为列表项或网格项的容器 + +## 示例 + +### 基础用法 + +最简单的卡片用法。 + +```jsx live +function Demo() { + return ( +
+ +

+ KubeSphere 是一个开源的容器平台,提供全栈的 IT 自动化运维能力, + 简化企业的 DevOps 工作流。 +

+
+
+ ); +} +``` + +### 带标题 + +通过 `sectionTitle` 属性添加卡片标题。 + +```jsx live +function Demo() { + return ( +
+ +

+ KubeSphere 是在 Kubernetes 之上构建的面向云原生应用的分布式操作系统, + 完全开源,支持多云与多集群管理,提供全栈的 IT 自动化运维能力。 +

+
+
+ ); +} +``` + +### 悬停效果 + +通过 `hoverable` 属性添加鼠标悬停时的阴影效果。 + +```jsx live +function Demo() { + return ( +
+ +

+ 鼠标悬停在卡片上时会显示更深的阴影效果, + 适用于可点击或可交互的卡片场景。 +

+
+
+ ); +} +``` + +### 内边距 + +通过 `padding` 属性控制卡片内容的内边距。 + +```jsx live +function Demo() { + return ( +
+ + +

padding: xs

+
+ +

padding: sm (默认)

+
+ +

padding: md

+
+ +

padding: lg

+
+ +

padding: 32px

+
+
+
+ ); +} +``` + +### 卡片列表 + +使用 Grid 组件创建卡片列表布局。 + +```jsx live +function Demo() { + const { Kubernetes, Docker, Cluster } = KubedIcons; + + const items = [ + { icon: Kubernetes, title: 'Kubernetes', desc: '容器编排平台' }, + { icon: Docker, title: 'Docker', desc: '容器运行时' }, + { icon: Cluster, title: '集群管理', desc: '多集群管理' }, + ]; + + return ( +
+ + {items.map((item) => ( + + + +
+
{item.title}
+
{item.desc}
+
+
+
+ ))} +
+
+ ); +} +``` + +### 统计卡片 + +展示统计数据的卡片示例。 + +```jsx live +function Demo() { + const stats = [ + { label: '运行中', value: 128, color: '#55bc8a' }, + { label: '已停止', value: 23, color: '#ca2621' }, + { label: '待处理', value: 45, color: '#f5a623' }, + ]; + + return ( +
+ + {stats.map((stat) => ( + +
+ {stat.value} +
+
+ {stat.label} +
+
+ ))} +
+
+ ); +} +``` + +### 带操作的卡片 + +卡片中包含操作按钮。 + +```jsx live +function Demo() { + const { Pen, Trash } = KubedIcons; + + return ( +
+ + +

+ 这是一个带有操作按钮的卡片示例,可以在卡片底部添加编辑、删除等操作。 +

+ + + + + +
+
+
+ ); +} +``` + +### 自定义样式 + +通过 `contentStyle` 和 `contentClassName` 自定义卡片内容样式。 + +```jsx live +function Demo() { + return ( +
+ + +

浅蓝色背景

+
+ +

带边框卡片

+
+
+
+ ); +} +``` + +### 嵌套卡片 + +卡片可以嵌套使用。 + +```jsx live +function Demo() { + return ( +
+ + +

外层卡片内容

+ +

嵌套的内层卡片

+
+
+
+
+ ); +} +``` + +## API + +### Card + +| 属性 | 说明 | 类型 | 默认值 | +| ---------------- | -------------------- | ------------------------------- | ------ | +| sectionTitle | 卡片标题 | `ReactNode` | - | +| padding | 内容内边距 | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| number` | `'sm'` | +| hoverable | 是否显示悬停效果 | `boolean` | `false` | +| contentStyle | 内容区域样式 | `CSSProperties` | - | +| contentClassName | 内容区域类名 | `string` | - | +| children | 卡片内容 | `ReactNode` | - | + +:::info + +**关于内边距**: +- 支持预设尺寸:`xs`、`sm`、`md`、`lg`、`xl` +- 也支持传入数字值(单位为 px) +- 默认值为 `sm` + +**关于标题**: +- `sectionTitle` 会显示在卡片内容的上方 +- 标题使用较深的颜色和加粗字体 +- 适合用于给卡片内容分类或命名 + +**关于悬停效果**: +- 设置 `hoverable` 后,鼠标悬停时卡片阴影会加深 +- 适用于可点击、可选择的卡片场景 +- 可以结合 `onClick` 事件使卡片可交互 + +::: + +## 使用建议 + +### 卡片布局 + +使用 Grid 或 Group 组件创建卡片网格布局: + +```jsx +// 使用 Group 横向排列 + + 卡片 1 + 卡片 2 + 卡片 3 + + +// 使用 Grid 创建响应式网格 + + 卡片 1 + 卡片 2 + 卡片 3 + +``` + +### 可点击卡片 + +创建可点击的卡片: + +```jsx + handleClick()} + style={{ cursor: 'pointer' }} +> + 点击此卡片 + +``` + +### 卡片背景 + +Card 组件默认有白色背景和阴影,适合放在灰色或其他颜色的容器中: + +```jsx +// 推荐:在灰色背景中使用 +
+ 内容 +
+ +// 不推荐:在白色背景中直接使用(阴影效果不明显) +
+ 内容 +
+``` + +### 内容组织 + +合理组织卡片内容: + +```jsx + + +
姓名:张三
+
邮箱:zhangsan@example.com
+ + +
+
+``` + +### 统计展示 + +用于展示统计数据: + +```jsx + +
1,234
+
总用户数
+
+``` diff --git a/docusaurus/docs/components/center.mdx b/docusaurus/docs/components/center.mdx new file mode 100644 index 00000000..e51df0bc --- /dev/null +++ b/docusaurus/docs/components/center.mdx @@ -0,0 +1,238 @@ +--- +sidebar_position: 3 +--- + +# Center 居中 + +将单个子元素在水平和垂直方向上居中的容器组件。 + +## 何时使用 + +- 需要将内容在容器中居中显示 +- 快速实现水平和垂直居中布局 +- 适用于各种场景的居中需求 + +## 示例 + +### 基础用法 + +Center 默认使用 flexbox 实现子元素的水平和垂直居中。 + +```jsx live +function Demo() { + return ( +
+
+ 居中内容 +
+
+ ); +} +``` + +### 不同高度 + +Center 会根据容器高度自动居中内容。 + +```jsx live +function Demo() { + return ( +
+

高度: 100px

+
+ 居中文本 +
+ +

高度: 200px

+
+ 居中文本 +
+
+ ); +} +``` + +### 居中按钮 + +常用于将按钮或按钮组居中显示。 + +```jsx live +function Demo() { + return ( +
+ + + + +
+ ); +} +``` + +### 居中图标 + +适合用于空状态、加载状态等场景。 + +```jsx live +function Demo() { + const { InformationDuotone, WarningDuotone, ErrorDuotone } = KubedIcons; + + return ( +
+

信息提示

+
+
+ +
暂无数据
+
+
+ +

警告提示

+
+
+ +
配置未完成
+
+
+ +

错误提示

+
+
+ +
加载失败
+
+
+
+ ); +} +``` + +### 全屏居中 + +通过设置容器高度为视口高度实现全屏居中。 + +```jsx live +function Demo() { + return ( +
+
+

欢迎使用

+

开始您的云原生之旅

+ +
+
+ ); +} +``` + +### 卡片居中 + +常用于卡片容器中的内容居中。 + +```jsx live +function Demo() { + const { KubernetesDuotone } = KubedIcons; + + return ( +
+
+
+ +
+
+

Kubernetes

+

容器编排平台

+
+
+ +
+
+ +
+
+

服务网格

+

微服务治理

+
+
+ +
+
+ +
+
+

应用商店

+

一键部署应用

+
+
+
+ ); +} +``` + +### 响应式居中 + +Center 组件天然支持响应式布局。 + +```jsx live +function Demo() { + return ( +
+
+

响应式内容

+

+ Center 组件会自动适应容器大小,无论内容如何变化,都能保持居中对齐。 +

+
+
+ ); +} +``` + +## API + +### Center 属性 + +| 属性 | 说明 | 类型 | 默认值 | +| --------- | ------------ | -------------------------------- | ------ | +| children | 要居中的内容 | `ReactNode` | - | +| className | 自定义类名 | `string` | - | +| style | 自定义样式 | `React.CSSProperties` | - | +| 其他 | 原生属性 | `HTMLAttributes` | - | + +:::info +Center 组件继承所有原生 HTML div 元素的属性(如 `onClick`、`onMouseEnter` 等)。 +::: diff --git a/docusaurus/docs/components/checkbox.mdx b/docusaurus/docs/components/checkbox.mdx new file mode 100644 index 00000000..b7f089cf --- /dev/null +++ b/docusaurus/docs/components/checkbox.mdx @@ -0,0 +1,390 @@ +--- +sidebar_position: 1 +--- + +# Checkbox 复选框 + +用于在多个选项中进行多选。 + +## 何时使用 + +- 在一组可选项中进行多选 +- 单独使用可以表示两种状态之间的切换 +- 用于表单中的多选场景 +- 需要全选、半选等复杂交互场景 + +## 示例 + +### 基础用法 + +基本的复选框使用,通过 `label` 设置文本内容。 + +```jsx live +function Demo() { + const [checked, setChecked] = React.useState(false); + + const handleChange = (e) => { + setChecked(e.target.checked); + }; + + return ( + + + +
+ 选项 A 状态: {checked ? '已选中' : '未选中'} +
+
+ ); +} +``` + +### 受控组件 + +通过 `checked` 属性控制复选框的选中状态。 + +```jsx live +function Demo() { + const [checked, setChecked] = React.useState(true); + + const toggle = () => { + setChecked(!checked); + }; + + return ( +
+ setChecked(e.target.checked)} + /> + +
+ ); +} +``` + +### 禁用状态 + +通过 `disabled` 属性禁用复选框。 + +```jsx live +function Demo() { + return ( + + + + + ); +} +``` + +### 半选状态 + +使用 `indeterminate` 属性表示部分选中状态,常用于全选场景。 + +```jsx live +function Demo() { + return ( + + + + + + ); +} +``` + +### 复选框组 + +使用 `CheckboxGroup` 组件实现一组复选框的管理。 + +```jsx live +function Demo() { + const [value, setValue] = React.useState(['kubernetes']); + + const handleChange = (values) => { + setValue(values); + }; + + return ( +
+ + + + + + +
+ 已选择: {value.length > 0 ? value.join(', ') : '无'} +
+
+ ); +} +``` + +### 复选框组 - 禁用 + +CheckboxGroup 可以统一禁用所有子选项。 + +```jsx live +function Demo() { + return ( + + + + + + ); +} +``` + +### 垂直排列 + +CheckboxGroup 默认使用 Group 组件水平排列,可以通过 `unstyled` 自定义布局。 + +```jsx live +function Demo() { + const [value, setValue] = React.useState(['frontend']); + + return ( + + + + + + + + + ); +} +``` + +### 全选功能 + +实现全选、半选、取消全选的交互。 + +```jsx live +function Demo() { + const allOptions = [ + { label: 'Kubernetes', value: 'k8s' }, + { label: 'Docker', value: 'docker' }, + { label: 'Istio', value: 'istio' }, + { label: 'Prometheus', value: 'prometheus' }, + ]; + + const [checkedList, setCheckedList] = React.useState(['k8s', 'docker']); + + const allValues = allOptions.map((item) => item.value); + const checkAll = checkedList.length === allOptions.length; + const indeterminate = checkedList.length > 0 && checkedList.length < allOptions.length; + + const onCheckAllChange = (e) => { + setCheckedList(e.target.checked ? allValues : []); + }; + + return ( +
+ + + + {allOptions.map((option) => ( + + ))} + +
+ 已选择 {checkedList.length} 项 +
+
+ ); +} +``` + +### 无标签 + +Checkbox 可以不传 `label` 属性,仅显示复选框。 + +```jsx live +function Demo() { + return ( + + + + + + ); +} +``` + +### 动态选项 + +根据数据动态生成复选框列表。 + +```jsx live +function Demo() { + const options = [ + { label: '集群管理', value: 'cluster', disabled: false }, + { label: '应用部署', value: 'app', disabled: false }, + { label: '监控告警', value: 'monitor', disabled: false }, + { label: '日志分析', value: 'log', disabled: true }, + { label: '权限管理', value: 'auth', disabled: false }, + ]; + + const [selectedValues, setSelectedValues] = React.useState(['cluster', 'app']); + + return ( +
+

请选择权限:

+ + + {options.map((option) => ( + + ))} + + +
+
已选择权限:
+
+ {selectedValues.length > 0 + ? options + .filter((opt) => selectedValues.includes(opt.value)) + .map((opt) => opt.label) + .join('、') + : '暂未选择'} +
+
+
+ ); +} +``` + +## API + +### Checkbox 属性 + +| 属性 | 说明 | 类型 | 默认值 | +| -------------- | ------------------------------------- | ---------------------------------- | ------- | +| label | 复选框文本标签 | `ReactNode` | - | +| value | 复选框的值(在 CheckboxGroup 中使用) | `string` | - | +| checked | 是否选中(受控) | `boolean` | - | +| defaultChecked | 默认是否选中(非受控) | `boolean` | `false` | +| indeterminate | 设置半选状态,优先级高于 checked | `boolean` | `false` | +| disabled | 是否禁用 | `boolean` | `false` | +| id | input 元素的 id,用于绑定 label | `string` | - | +| onChange | 变化时的回调函数 | `(e: Event) => void` | - | +| 其他 | 原生属性 | `HTMLAttributes` | - | + +:::info + +Checkbox 组件继承所有原生 HTML input[type="checkbox"] 元素的属性(如 `onClick`、`onFocus` 等)。 + +**关于 indeterminate**: + +- `indeterminate` 状态用于表示"部分选中",通常在全选场景中使用 +- 当 `indeterminate` 为 `true` 时,复选框显示为半选状态(横线) +- `indeterminate` 优先级高于 `checked`,设置为 `true` 时会覆盖 `checked` 的效果 + +**在 CheckboxGroup 中使用**: + +- 在 CheckboxGroup 中,不要使用 `checked` 属性,应通过 CheckboxGroup 的 `value` 控制 +- 必须设置 `value` 属性以标识每个选项 + +::: + +### CheckboxGroup 属性 + +| 属性 | 说明 | 类型 | 默认值 | +| ------------ | ------------------------------------- | ----------------------------- | ------- | +| value | 指定选中的选项(受控) | `string[]` | - | +| defaultValue | 默认选中的选项(非受控) | `string[]` | `[]` | +| disabled | 是否禁用所有复选框 | `boolean` | `false` | +| onChange | 变化时的回调函数 | `(values: string[]) => void` | - | +| unstyled | 是否移除默认样式(不使用 Group 包裹) | `boolean` | `false` | +| 其他 | 原生属性 | `HTMLAttributes` | - | + +:::info + +**关于布局**: + +- CheckboxGroup 默认使用 Group 组件水平排列子元素 +- 设置 `unstyled={true}` 可移除 Group 包裹,自定义布局方式 +- 自定义布局时,可以配合 Group 组件的 `direction="column"` 实现垂直排列 + +**受控与非受控**: + +- 使用 `value` + `onChange` 实现受控组件 +- 使用 `defaultValue` 实现非受控组件 +- CheckboxGroup 的 `value` 是一个字符串数组,包含所有选中项的 `value` 值 + +::: + +## 使用建议 + +### 受控与非受控 + +根据场景选择合适的使用方式: + +```jsx +// 非受控:适用于简单场景,不需要外部控制状态 +; + +// 受控:适用于需要外部控制或复杂交互的场景 +const [checked, setChecked] = React.useState(false); + setChecked(e.target.checked)} />; +``` + +### 全选实现 + +实现全选功能的推荐方式: + +```jsx +const allChecked = checkedList.length === allOptions.length; +const indeterminate = checkedList.length > 0 && checkedList.length < allOptions.length; + + setCheckedList(e.target.checked ? allValues : [])} +/>; +``` + +### CheckboxGroup 布局 + +自定义 CheckboxGroup 的布局方式: + +```jsx +// 垂直排列 + + + + + + + +// 网格布局 + + + + + + +``` diff --git a/docusaurus/docs/components/collapse.mdx b/docusaurus/docs/components/collapse.mdx new file mode 100644 index 00000000..ec6c6696 --- /dev/null +++ b/docusaurus/docs/components/collapse.mdx @@ -0,0 +1,404 @@ +--- +sidebar_position: 1 +--- + +# Collapse 折叠面板 + +可以折叠/展开的内容区域组件。 + +## 何时使用 + +- 对复杂区域进行分组和隐藏,保持页面整洁 +- 手风琴模式,只允许展开一个面板 +- 需要展示可折叠的配置项或详情信息 +- FAQ 问答列表等场景 + +## 示例 + +### 基础用法 + +可以同时展开多个面板。 + +```jsx live +function Demo() { + const { Panel } = Collapse; + + return ( + + +

这是面板 1 的内容。可以放置任意内容,包括文本、表单、列表等。

+
+ +

这是面板 2 的内容。折叠面板可以帮助组织和隐藏复杂的内容。

+
+ +

这是面板 3 的内容。用户可以按需展开查看详细信息。

+
+
+ ); +} +``` + +### 手风琴模式 + +通过 `accordion` 属性设置手风琴模式,每次只能展开一个面板。 + +```jsx live +function Demo() { + const { Panel } = Collapse; + + return ( + + +

+ Kubernetes 是一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。 +

+
+ +

+ Docker 是一个开源的应用容器引擎,让开发者可以打包应用及依赖到一个可移植的容器中。 +

+
+ +

Helm 是 Kubernetes 的包管理器,帮助您管理 Kubernetes 应用程序。

+
+
+ ); +} +``` + +### 受控模式 + +通过 `activeKey` 和 `onChange` 实现受控组件。 + +```jsx live +function Demo() { + const { Panel } = Collapse; + const [activeKey, setActiveKey] = React.useState(['1']); + + return ( +
+ + +

面板 1 的内容

+
+ +

面板 2 的内容

+
+ +

面板 3 的内容

+
+
+ + + + +
+ ); +} +``` + +### 可折叠触发区域 + +通过 `collapsible` 属性设置面板的可折叠触发区域。 + +```jsx live +function Demo() { + const { Panel } = Collapse; + + return ( + + +

点击面板头部任意位置都可以触发折叠/展开。

+
+ +

只有点击标题文字才能触发折叠/展开。

+
+ +

只有点击箭头图标才能触发折叠/展开。

+
+ +

此面板无法折叠/展开。

+
+
+ ); +} +``` + +### 隐藏箭头图标 + +通过 `showArrow={false}` 隐藏面板的箭头图标。 + +```jsx live +function Demo() { + const { Panel } = Collapse; + + return ( + + +

这个面板显示箭头图标。

+
+ +

这个面板隐藏了箭头图标。

+
+
+ ); +} +``` + +### 额外内容 + +通过 `extra` 属性在面板头部右侧添加额外内容。 + +```jsx live +function Demo() { + const { Panel } = Collapse; + const { FolderSettingDuotone, Trash } = KubedIcons; + + return ( + + e.stopPropagation()}> + + + } + > +

这个面板在右侧有一个设置按钮。

+
+ e.stopPropagation()} + > + + + } + > +

这个面板在右侧有一个删除按钮。

+
+
+ ); +} +``` + +### 自定义展开图标 + +通过 `expandIcon` 属性自定义展开/折叠图标。 + +```jsx live +function Demo() { + const { Panel } = Collapse; + const { Add, Substract } = KubedIcons; + + return ( + (isActive ? : )} + > + +

使用加号/减号作为展开图标。

+
+ +

展开时显示减号,折叠时显示加号。

+
+
+ ); +} +``` + +### 嵌套折叠面板 + +折叠面板可以嵌套使用。 + +```jsx live +function Demo() { + const { Panel } = Collapse; + + return ( + + + + +

嵌套的面板内容 1-1

+
+ +

嵌套的面板内容 1-2

+
+
+
+ +

外层面板 2 的内容

+
+
+ ); +} +``` + +## API + +### Collapse + +| 属性 | 说明 | 类型 | 默认值 | +| -------------------- | ------------------------ | ------------------------------------------ | ------- | +| activeKey | 当前展开的面板 key | `string \| string[] \| number \| number[]` | - | +| defaultActiveKey | 默认展开的面板 key | `string \| string[] \| number \| number[]` | - | +| accordion | 是否为手风琴模式 | `boolean` | `false` | +| expandIcon | 自定义展开图标 | `(panelProps) => ReactNode` | - | +| collapsible | 所有面板的可折叠触发区域 | `'header' \| 'icon' \| 'disabled'` | - | +| destroyInactivePanel | 销毁折叠隐藏的面板 | `boolean` | `false` | +| onChange | 切换面板时的回调 | `(key: string \| string[]) => void` | - | + +### Collapse.Panel + +| 属性 | 说明 | 类型 | 默认值 | +| ----------- | ---------------- | ---------------------------------- | ------- | +| key | 面板唯一标识 | `string \| number` | - | +| header | 面板头部内容 | `ReactNode` | - | +| extra | 头部右侧额外内容 | `ReactNode` | - | +| showArrow | 是否显示箭头图标 | `boolean` | `true` | +| forceRender | 强制渲染面板内容 | `boolean` | `false` | +| collapsible | 可折叠触发区域 | `'header' \| 'icon' \| 'disabled'` | - | + +:::info + +**关于 activeKey**: + +- 非手风琴模式下,`activeKey` 为数组,可以同时展开多个面板 +- 手风琴模式下,`activeKey` 为单个值,只能展开一个面板 +- 使用 `defaultActiveKey` 设置初始展开状态(非受控) +- 使用 `activeKey` + `onChange` 实现受控模式 + +**关于 collapsible**: + +- `header`:只有点击头部文字才能触发折叠 +- `icon`:只有点击箭头图标才能触发折叠 +- `disabled`:禁用折叠功能 +- 可以在 Collapse 上统一设置,也可以在 Panel 上单独设置 + +**关于 expandIcon**: + +- 函数接收 `panelProps` 参数,包含 `isActive` 属性 +- 可以根据 `isActive` 返回不同的图标 + +::: + +## 使用建议 + +### 手风琴 vs 多面板 + +根据使用场景选择合适的模式: + +```jsx +// 手风琴模式:内容互斥时使用 + + ... + ... + + +// 多面板模式:内容可以同时查看 + + ... + ... + +``` + +### 额外操作按钮 + +在面板头部添加操作按钮时,需要阻止事件冒泡: + +```jsx + { + e.stopPropagation(); // 阻止触发面板折叠 + handleAction(); + }} + > + 操作 + + } +> + 内容 + +``` + +### 默认展开 + +根据业务需求设置默认展开的面板: + +```jsx +// 展开第一个面板 + + ... + + +// 展开多个面板 + + ... + + +// 全部折叠 + + ... + +``` + +### 动态面板 + +动态生成折叠面板: + +```jsx +const items = [ + { key: '1', title: '面板 1', content: '内容 1' }, + { key: '2', title: '面板 2', content: '内容 2' }, +]; + + + {items.map((item) => ( + + {item.content} + + ))} +; +``` + +### 配置表单 + +使用折叠面板组织复杂的配置表单: + +```jsx + + +
+ + + + +