From 7c8eefeb864e25315c0c305dd9cfe11d063c3cca Mon Sep 17 00:00:00 2001 From: pomelo-nwu Date: Wed, 16 Apr 2025 14:33:37 +0800 Subject: [PATCH 01/14] refactor: optimize EditableText component with consolidated state management and enhanced disabled state visuals using Ant Design tokens --- .../src/EditableText/index.en-US.md | 86 ++++++++ .../src/EditableText/index.md | 86 +++++++- .../src/EditableText/index.tsx | 192 ++++++++++++++---- 3 files changed, 328 insertions(+), 36 deletions(-) create mode 100644 packages/studio-components/src/EditableText/index.en-US.md diff --git a/packages/studio-components/src/EditableText/index.en-US.md b/packages/studio-components/src/EditableText/index.en-US.md new file mode 100644 index 000000000..926bb8c3b --- /dev/null +++ b/packages/studio-components/src/EditableText/index.en-US.md @@ -0,0 +1,86 @@ +--- +title: EditableText +group: + title: Data Display + order: 1 +--- + +# EditableText + +An editable text component that supports double-click editing, enter confirmation, and blur saving. + +## When To Use + +- When you need to let users edit text content directly +- When you need to display editable text fields +- When you need to implement Excel-like cell editing functionality + +## Examples + +### Basic Usage + +```tsx +import React from 'react'; +import { EditableText } from '@graphscope/studio-components'; + +const Demo = () => { + const handleTextChange = (text: string) => { + console.log('Text updated:', text); + }; + + return ; +}; + +export default Demo; +``` + +### Disabled State + +```tsx +import React from 'react'; +import { EditableText } from '@graphscope/studio-components'; + +const Demo = () => { + return {}} disabled />; +}; + +export default Demo; +``` + +### Custom Style + +```tsx +import React from 'react'; +import { EditableText } from '@graphscope/studio-components'; + +const Demo = () => { + return ( + {}} + style={{ + color: 'blue', + fontSize: '16px', + }} + maxWidth={200} + minWidth={100} + /> + ); +}; + +export default Demo; +``` + +## API + +### EditableText + +| Property | Description | Type | Default | +| ------------ | -------------------- | ------------------------ | ------- | +| text | Initial text content | `string` | - | +| onTextChange | Text change callback | `(text: string) => void` | - | +| id | Component identifier | `string` | - | +| style | Custom styles | `React.CSSProperties` | - | +| disabled | Whether disabled | `boolean` | `false` | +| maxWidth | Maximum width (px) | `number` | `100` | +| minWidth | Minimum width (px) | `number` | `60` | diff --git a/packages/studio-components/src/EditableText/index.md b/packages/studio-components/src/EditableText/index.md index 28d40100f..0273cc50e 100644 --- a/packages/studio-components/src/EditableText/index.md +++ b/packages/studio-components/src/EditableText/index.md @@ -1,8 +1,90 @@ --- -order: 1 -title: 可编辑文本 +title: EditableText 可编辑文本 +group: + title: 数据展示 + order: 1 --- +# EditableText 可编辑文本 + +可编辑文本组件,支持双击编辑、回车确认、失焦保存等功能。 + +## 何时使用 + +- 需要让用户直接编辑文本内容时 +- 需要展示可编辑的文本字段时 +- 需要实现类似 Excel 单元格编辑功能时 + +## 代码演示 + +### 基础用法 + +```tsx +import React from 'react'; +import { EditableText } from '@graphscope/studio-components'; + +const Demo = () => { + const handleTextChange = (text: string) => { + console.log('文本已更新:', text); + }; + + return ; +}; + +export default Demo; +``` + +### 禁用状态 + +```tsx +import React from 'react'; +import { EditableText } from '@graphscope/studio-components'; + +const Demo = () => { + return {}} disabled />; +}; + +export default Demo; +``` + +### 自定义样式 + +```tsx +import React from 'react'; +import { EditableText } from '@graphscope/studio-components'; + +const Demo = () => { + return ( + {}} + style={{ + color: 'blue', + fontSize: '16px', + }} + maxWidth={200} + minWidth={100} + /> + ); +}; + +export default Demo; +``` + +## API + +### EditableText + +| 参数 | 说明 | 类型 | 默认值 | +| ------------ | -------------------- | ------------------------ | ------- | +| text | 初始文本内容 | `string` | - | +| onTextChange | 文本变化时的回调函数 | `(text: string) => void` | - | +| id | 组件唯一标识符 | `string` | - | +| style | 自定义样式 | `React.CSSProperties` | - | +| disabled | 是否禁用编辑功能 | `boolean` | `false` | +| maxWidth | 最大宽度(像素) | `number` | `100` | +| minWidth | 最小宽度(像素) | `number` | `60` | + ```jsx import React, { useState } from 'react'; import { EditableText } from '@graphscope/studio-components'; diff --git a/packages/studio-components/src/EditableText/index.tsx b/packages/studio-components/src/EditableText/index.tsx index 9bbcbef0e..b597f80d5 100644 --- a/packages/studio-components/src/EditableText/index.tsx +++ b/packages/studio-components/src/EditableText/index.tsx @@ -1,36 +1,110 @@ import React, { useState, useRef, useEffect } from 'react'; import { debounce } from '../Utils/index'; -const EditableText = ({ text, onTextChange, id, style = {}, disabled }) => { - const [isEditing, setIsEditing] = useState(false); - const [editableText, setEditableText] = useState(text); +import { theme } from 'antd'; - const inputRef = useRef(null); +/** + * EditableText 组件的状态接口 + */ +interface EditableTextState { + isEditing: boolean; + editableText: string; + isHovered: boolean; + textWidth: number; +} +/** + * EditableText 组件的属性接口 + */ +interface EditableTextProps { + /** 初始文本内容 */ + text: string; + /** 文本变化时的回调函数 */ + onTextChange: (text: string) => void; + /** 组件唯一标识符 */ + id?: string; + /** 自定义样式 */ + style?: React.CSSProperties; + /** 是否禁用编辑功能 */ + disabled?: boolean; + /** 最大宽度 */ + maxWidth?: number; + /** 最小宽度 */ + minWidth?: number; + /** 文本对齐方式 */ + textAlign?: 'left' | 'center' | 'right'; + /** 悬停时是否显示背景色 */ + hoverBackground?: boolean; +} + +/** + * 可编辑文本组件 + * + * 支持双击编辑、回车确认、失焦保存等功能 + * 可以通过 style 属性自定义样式 + * 支持禁用状态 + */ +const EditableText = (props: EditableTextProps) => { + const { + text, + onTextChange, + id, + style = {}, + disabled = false, + maxWidth = 100, + minWidth = 60, + textAlign = 'center', + hoverBackground = true, + } = props; + + // 获取 antd 主题 token + const { token } = theme.useToken(); + + // 合并状态管理 + const [state, setState] = useState({ + isEditing: false, + editableText: text, + isHovered: false, + textWidth: 0, + }); + + const inputRef = useRef(null); + const textRef = useRef(null); + + // 当进入编辑模式时自动聚焦 useEffect(() => { - if (isEditing && inputRef.current) { - // 当进入编辑模式时聚焦到输入框 + if (state.isEditing && inputRef.current) { inputRef.current.focus(); } - }, [isEditing]); + }, [state.isEditing]); + + // 同步外部文本变化 useEffect(() => { - setEditableText(text); + setState(prev => ({ ...prev, editableText: text })); }, [text]); - const handleDoubleClick = e => { + // 测量文本宽度 + useEffect(() => { + if (textRef.current) { + const width = textRef.current.offsetWidth; + setState(prev => ({ ...prev, textWidth: width })); + } + }, [state.editableText]); + + // 防抖处理文本更新 + const debouncedOnTextChange = React.useMemo(() => debounce(onTextChange, 300), [onTextChange]); + + const handleDoubleClick = (e: React.MouseEvent) => { e.stopPropagation(); e.preventDefault(); if (disabled) return; - // 开启编辑模式 - setIsEditing(true); + setState(prev => ({ ...prev, isEditing: true })); }; - const handleChange = e => { - // 更新输入框中的文本 - setEditableText(e.target.value); + const handleChange = (e: React.ChangeEvent) => { + setState(prev => ({ ...prev, editableText: e.target.value })); }; - const handleKeyDown = e => { - // 当用户按下回车键时结束编辑 + const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { handleSave(); e.preventDefault(); @@ -38,54 +112,104 @@ const EditableText = ({ text, onTextChange, id, style = {}, disabled }) => { }; const handleBlur = () => { - // 当用户点击输入框外部时结束编辑 handleSave(); }; const handleSave = () => { - // 将文本保存并结束编辑模式 - onTextChange(editableText); - setIsEditing(false); + const { editableText } = state; + debouncedOnTextChange(editableText); + setState(prev => ({ ...prev, isEditing: false })); + }; + + const handleMouseEnter = () => { + if (!disabled) { + setState(prev => ({ ...prev, isHovered: true })); + } + }; + + const handleMouseLeave = () => { + setState(prev => ({ ...prev, isHovered: false })); + }; + + const commonStyles: React.CSSProperties = { + textAlign, + fontSize: '12px', + transition: 'all 0.2s ease', + ...style, + }; + + const containerStyle: React.CSSProperties = { + position: 'relative', + minWidth: `${minWidth}px`, + maxWidth: `${maxWidth}px`, + height: '24px', // 固定高度避免抖动 + borderRadius: '4px', + overflow: 'hidden', + }; + + const getBackgroundStyle = () => { + if (disabled) return { backgroundColor: token.colorBgContainerDisabled }; + if (state.isEditing) return { backgroundColor: 'rgba(0, 0, 0, 0.04)' }; + if (state.isHovered && hoverBackground) return { backgroundColor: 'rgba(0, 0, 0, 0.02)' }; + return {}; + }; + + const getTextStyle = () => { + if (disabled) { + return { + color: token.colorTextDisabled, + cursor: 'not-allowed', + }; + } + return { + cursor: 'pointer', + }; }; return ( -
- {isEditing ? ( +
+ {state.isEditing ? ( ) : (
- {editableText} + {state.editableText}
)}
From f35f67de448c98186642327dba670751e067fbae Mon Sep 17 00:00:00 2001 From: pomelo-nwu Date: Wed, 16 Apr 2025 14:38:21 +0800 Subject: [PATCH 02/14] docs: move AI-assisted component optimization prompt to studio-components package --- .../studio-components/ai-coding-prompt.md | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 packages/studio-components/ai-coding-prompt.md diff --git a/packages/studio-components/ai-coding-prompt.md b/packages/studio-components/ai-coding-prompt.md new file mode 100644 index 000000000..839a44ce5 --- /dev/null +++ b/packages/studio-components/ai-coding-prompt.md @@ -0,0 +1,107 @@ +# AI 辅助组件优化与开发协作 Prompt + +## 组件优化指南 + +以下是一个用于指导 AI 助手优化 React 组件的 prompt 模板,基于我们在 `EditableText` 组件优化过程中的经验总结: + +``` +请帮我优化 [组件名称] 组件,重点关注以下几个方面: + +1. 状态管理优化: + - 合并相关状态到一个统一的状态对象中 + - 使用 TypeScript 接口定义状态类型 + - 确保状态更新逻辑清晰且一致 + +2. 性能优化: + - 减少不必要的重渲染 + - 使用 useMemo 和 useCallback 优化函数和计算 + - 优化 useEffect 依赖项 + +3. 用户体验优化: + - 增强视觉反馈 + - 优化交互体验 + - 确保无障碍性 + +4. 代码质量提升: + - 添加清晰的类型定义 + - 添加详细的注释 + - 遵循一致的代码风格 + - 提取可复用的逻辑到自定义 hooks + +5. 样式优化: + - 使用设计系统的 token(如 Ant Design) + - 确保响应式设计 + - 优化动画和过渡效果 + +6. 文档完善: + - 创建中英文文档 + - 提供使用示例 + - 详细说明 API 和属性 + +请逐步优化,每次专注于一个方面,并在每次更改后等待我的反馈。 +``` + +## 开发协作模式 + +以下是我们与 AI 助手协作开发的有效模式: + +### 1. 渐进式优化 + +- 一次专注于一个方面的优化 +- 每次更改后等待反馈 +- 根据反馈调整优化方向 + +### 2. 代码审查流程 + +- AI 提供优化建议 +- 开发者审查并给出反馈 +- AI 根据反馈进行调整 +- 最终确认后提交代码 + +### 3. 文档同步 + +- 代码更改与文档更新同步进行 +- 确保中英文文档一致 +- 提供详细的使用示例 + +### 4. Git 工作流 + +- 使用清晰的英文提交信息 +- 遵循 conventional commits 规范 +- 相关文件一起提交(组件代码、文档等) + +## 实际案例:EditableText 组件优化 + +我们通过以下步骤优化了 `EditableText` 组件: + +1. **状态管理优化**: + + - 将 `isHovered` 和 `textWidth` 合并到 `EditableTextState` 接口中 + - 使用 `setState` 统一管理状态更新 + +2. **视觉反馈增强**: + + - 使用 Ant Design 的 token 增强禁用状态视觉效果 + - 添加悬停效果 + - 优化编辑状态的视觉反馈 + +3. **代码结构优化**: + + - 提取样式逻辑到独立函数 + - 添加详细的类型定义和注释 + - 优化事件处理函数 + +4. **文档完善**: + - 创建中英文文档 + - 提供使用示例 + - 详细说明 API 和属性 + +## 使用建议 + +1. **明确需求**:在开始优化前,明确说明需要优化的具体方面 +2. **渐进式改进**:一次专注于一个方面,避免一次性做太多更改 +3. **及时反馈**:对 AI 的每次更改给出及时反馈,指导优化方向 +4. **保持一致性**:确保代码风格、文档格式和提交信息的一致性 +5. **同步文档**:代码更改与文档更新同步进行 + +通过遵循这个 prompt 和协作模式,您可以更有效地利用 AI 助手优化组件,提高开发效率和代码质量。 From 2a4d3ba37ce791996457913040f1d189c961d0ee Mon Sep 17 00:00:00 2001 From: pomelo-nwu Date: Wed, 16 Apr 2025 15:25:36 +0800 Subject: [PATCH 03/14] feat: optimize CollapseCard component and add documentation --- .../src/CollapseCard/index.en-US.md | 87 ++++++++++++++ .../src/CollapseCard/index.md | 89 ++++++++++++++ .../src/CollapseCard/index.tsx | 111 ++++++++++++++++-- 3 files changed, 274 insertions(+), 13 deletions(-) create mode 100644 packages/studio-components/src/CollapseCard/index.en-US.md create mode 100644 packages/studio-components/src/CollapseCard/index.md diff --git a/packages/studio-components/src/CollapseCard/index.en-US.md b/packages/studio-components/src/CollapseCard/index.en-US.md new file mode 100644 index 000000000..580a64c3f --- /dev/null +++ b/packages/studio-components/src/CollapseCard/index.en-US.md @@ -0,0 +1,87 @@ +--- +title: CollapseCard +group: + title: Data Display + order: 1 +--- + +# CollapseCard + +A collapsible card component that supports custom title, content and styles. + +## When To Use + +- When you need to display collapsible content areas +- When you need to save page space while maintaining content accessibility +- When you need to group related content + +## Examples + +### Basic Usage + +```tsx +import React from 'react'; +import { CollapseCard } from '@graphscope/studio-components'; + +const Demo = () => { + return ( + +
This is the card content
+
+ ); +}; + +export default Demo; +``` + +### With Tooltip + +```tsx +import React from 'react'; +import { CollapseCard } from '@graphscope/studio-components'; + +const Demo = () => { + return ( + +
This is the card content
+
+ ); +}; + +export default Demo; +``` + +### With Border + +```tsx +import React from 'react'; +import { CollapseCard } from '@graphscope/studio-components'; + +const Demo = () => { + return ( + +
This is the card content
+
+ ); +}; + +export default Demo; +``` + +## API + +### CollapseCard + +| Property | Description | Type | Default | +| --------------- | ------------------------------------- | ----------------------------- | ------- | +| bordered | Whether to show border | `boolean` | `false` | +| ghost | Whether to use transparent background | `boolean` | `true` | +| title | Card title | `React.ReactNode` | - | +| children | Card content | `React.ReactNode` | - | +| defaultCollapse | Whether to collapse by default | `boolean` | `false` | +| tooltip | Tooltip content | `React.ReactNode` | - | +| style | Custom style | `React.CSSProperties` | - | +| className | Custom class name | `string` | - | +| onChange | Callback when expand/collapse | `(isActive: boolean) => void` | - | + + diff --git a/packages/studio-components/src/CollapseCard/index.md b/packages/studio-components/src/CollapseCard/index.md new file mode 100644 index 000000000..72f9d7fa8 --- /dev/null +++ b/packages/studio-components/src/CollapseCard/index.md @@ -0,0 +1,89 @@ +--- +title: CollapseCard 可折叠卡片 +group: + title: 数据展示 + order: 1 +--- + +# CollapseCard 可折叠卡片 + +一个可折叠的卡片组件,支持自定义标题、内容和样式。 + +## 何时使用 + +- 需要展示可折叠的内容区域时 +- 需要节省页面空间,同时保持内容可访问性时 +- 需要分组展示相关内容时 + +## 代码演示 + +### 基础用法 + +```tsx +import React from 'react'; +import { CollapseCard } from '@graphscope/studio-components'; + +const Demo = () => { + return ( + +
这是卡片的内容
+
+ ); +}; + +export default Demo; +``` + +### 带提示信息 + +```tsx +import React from 'react'; +import { CollapseCard } from '@graphscope/studio-components'; + +const Demo = () => { + return ( + +
这是卡片的内容
+
+ ); +}; + +export default Demo; +``` + +### 带边框 + +```tsx +import React from 'react'; +import { CollapseCard } from '@graphscope/studio-components'; + +const Demo = () => { + return ( + +
这是卡片的内容
+
+ ); +}; + +export default Demo; +``` + +## API + +### CollapseCard + +| 参数 | 说明 | 类型 | 默认值 | +| --------------- | ----------------- | ----------------------------- | ------- | +| bordered | 是否显示边框 | `boolean` | `false` | +| ghost | 是否透明背景 | `boolean` | `true` | +| title | 卡片标题 | `React.ReactNode` | - | +| children | 卡片内容 | `React.ReactNode` | - | +| defaultCollapse | 默认是否折叠 | `boolean` | `false` | +| tooltip | 提示信息 | `React.ReactNode` | - | +| style | 自定义样式 | `React.CSSProperties` | - | +| className | 自定义类名 | `string` | - | +| onChange | 展开/收起时的回调 | `(isActive: boolean) => void` | - | + +``` + +``` diff --git a/packages/studio-components/src/CollapseCard/index.tsx b/packages/studio-components/src/CollapseCard/index.tsx index ed4dc906e..8bb2b4df4 100644 --- a/packages/studio-components/src/CollapseCard/index.tsx +++ b/packages/studio-components/src/CollapseCard/index.tsx @@ -1,47 +1,132 @@ -import React, { useState } from 'react'; +import React, { useMemo, useCallback } from 'react'; import { CaretRightOutlined, QuestionCircleOutlined } from '@ant-design/icons'; -import { Collapse, theme, Flex, Typography, Select, Space, Tooltip } from 'antd'; +import { Collapse, theme, Flex, Typography, Space, Tooltip } from 'antd'; +import type { CollapseProps } from 'antd'; import { useDynamicStyle } from '../hooks/useDynamicStyle'; -interface IAdvancedSettingProps { + +/** + * CollapseCard 组件的属性接口 + */ +export interface ICollapseCardProps { + /** 是否显示边框 */ bordered?: boolean; + /** 是否透明背景 */ ghost?: boolean; + /** 卡片标题 */ title: React.ReactNode; + /** 卡片内容 */ children: React.ReactNode; + /** 默认是否折叠 */ defaultCollapse?: boolean; + /** 提示信息 */ tooltip?: React.ReactNode; + /** 自定义样式 */ style?: React.CSSProperties; + /** 自定义类名 */ + className?: string; + /** 展开/收起时的回调 */ + onChange?: (isActive: boolean) => void; } -const CollapseCard: React.FunctionComponent = props => { - const { bordered, children, title, defaultCollapse, tooltip, style = {} } = props; +/** + * 可折叠卡片组件 + * @description 一个可折叠的卡片组件,支持自定义标题、内容和样式 + */ +const CollapseCard: React.FC = ({ + bordered = false, + ghost = true, + children, + title, + defaultCollapse = false, + tooltip, + style = {}, + className = '', + onChange, +}) => { + const { token } = theme.useToken(); const id = 'Studio-Collapse-Card'; const defaultActiveKey = defaultCollapse ? [] : [id]; + + // 使用 useMemo 优化样式计算 + const cardStyle = useMemo( + () => ({ + ...style, + backgroundColor: token.colorBgContainer, + borderRadius: token.borderRadiusLG, + boxShadow: bordered ? token.boxShadowTertiary : 'none', + }), + [style, token, bordered], + ); + + // 使用 useMemo 优化标题样式 + const titleStyle = useMemo( + () => ({ + margin: 0, + color: token.colorTextHeading, + fontSize: token.fontSizeLG, + fontWeight: token.fontWeightStrong, + }), + [token], + ); + + // 使用 useMemo 优化图标样式 + const iconStyle = useMemo( + () => ({ + transition: `transform ${token.motionDurationMid} ease`, + fontSize: token.fontSizeLG, + }), + [token], + ); + + // 动态样式注入 useDynamicStyle( - `.${id} .ant-collapse-header {padding:0px !important;} - .${id} .ant-collapse-content-box {padding:12px 0px !important;} + `.${id} .ant-collapse-header { + padding: ${token.padding}px !important; + transition: all ${token.motionDurationMid} ease; + } + .${id} .ant-collapse-content-box { + padding: ${token.padding}px ${token.paddingLG}px !important; + } `, id, ); + + // 使用 useCallback 优化事件处理函数 + const handleChange = useCallback( + (activeKeys: string | string[]) => { + const isActive = Array.isArray(activeKeys) ? activeKeys.includes(id) : activeKeys === id; + onChange?.(isActive); + }, + [id, onChange], + ); + + // 使用 useCallback 优化展开图标渲染函数 + const renderExpandIcon = useCallback( + (props: { isActive?: boolean }) => , + [iconStyle], + ); + return ( } + onChange={handleChange} + expandIcon={renderExpandIcon} items={[ { key: id, label: ( - + {title} {tooltip && ( - + )} From 5004a60bc28441b085d9e69bdebc1a253b44fea9 Mon Sep 17 00:00:00 2001 From: pomelo-nwu Date: Wed, 16 Apr 2025 15:33:25 +0800 Subject: [PATCH 04/14] feat: optimize EmptyCanvas component for responsive design --- .../src/EmptyCanvas/index.en-US.md | 85 +++++++++++++++++++ .../src/EmptyCanvas/index.md | 84 +++++++++++++++++- 2 files changed, 167 insertions(+), 2 deletions(-) create mode 100644 packages/studio-components/src/EmptyCanvas/index.en-US.md diff --git a/packages/studio-components/src/EmptyCanvas/index.en-US.md b/packages/studio-components/src/EmptyCanvas/index.en-US.md new file mode 100644 index 000000000..d4d949997 --- /dev/null +++ b/packages/studio-components/src/EmptyCanvas/index.en-US.md @@ -0,0 +1,85 @@ +--- +title: EmptyCanvas +group: + title: Data Display + order: 2 +--- + +# EmptyCanvas + +A component for displaying empty states with customizable description and styles. + +## When To Use + +- When there is no data to display +- When you need to show an empty state message +- When you need to customize the empty state style + +## Examples + +### Basic Usage + +```tsx +import React from 'react'; +import { EmptyCanvas } from '@graphscope/studio-components'; + +const Demo = () => { + return ; +}; + +export default Demo; +``` + +### Custom Description + +```tsx +import React from 'react'; +import { EmptyCanvas } from '@graphscope/studio-components'; + +const Demo = () => { + return ; +}; + +export default Demo; +``` + +### Custom Style + +```tsx +import React from 'react'; +import { EmptyCanvas } from '@graphscope/studio-components'; + +const Demo = () => { + return ; +}; + +export default Demo; +``` + +### Custom Image Size + +```tsx +import React from 'react'; +import { EmptyCanvas } from '@graphscope/studio-components'; + +const Demo = () => { + return ; +}; + +export default Demo; +``` + +## API + +### EmptyCanvas + +| Property | Description | Type | Default | +| ----------- | ----------------------------------------- | --------------------------- | ------------ | +| description | Empty state description | `string \| React.ReactNode` | `'暂无数据'` | +| style | Custom style | `React.CSSProperties` | - | +| className | Custom class name | `string` | - | +| imageSize | Image size, can be a number or percentage | `number \| string` | `'60%'` | + +``` + +``` diff --git a/packages/studio-components/src/EmptyCanvas/index.md b/packages/studio-components/src/EmptyCanvas/index.md index df5f6c93d..e39c3f6a5 100644 --- a/packages/studio-components/src/EmptyCanvas/index.md +++ b/packages/studio-components/src/EmptyCanvas/index.md @@ -1,4 +1,84 @@ -# EmptyCanvas +--- +title: EmptyCanvas 空画布 +group: + title: 数据展示 + order: 2 +--- + +# EmptyCanvas 空画布 + +用于展示空状态的组件,支持自定义描述文本和样式。 + +## 何时使用 + +- 当没有数据需要展示时 +- 当需要展示空状态提示时 +- 当需要自定义空状态样式时 + +## 代码演示 + +### 基础用法 + +```tsx +import React from 'react'; +import { EmptyCanvas } from '@graphscope/studio-components'; + +const Demo = () => { + return ; +}; + +export default Demo; +``` + +### 自定义描述 + +```tsx +import React from 'react'; +import { EmptyCanvas } from '@graphscope/studio-components'; + +const Demo = () => { + return ; +}; + +export default Demo; +``` + +### 自定义样式 + +```tsx +import React from 'react'; +import { EmptyCanvas } from '@graphscope/studio-components'; + +const Demo = () => { + return ; +}; + +export default Demo; +``` + +### 自定义图片大小 + +```tsx +import React from 'react'; +import { EmptyCanvas } from '@graphscope/studio-components'; + +const Demo = () => { + return ; +}; + +export default Demo; +``` + +## API + +### EmptyCanvas + +| 参数 | 说明 | 类型 | 默认值 | +| ----------- | ---------------------------- | --------------------------- | ------------ | +| description | 空状态描述文本 | `string \| React.ReactNode` | `'暂无数据'` | +| style | 自定义样式 | `React.CSSProperties` | - | +| className | 自定义类名 | `string` | - | +| imageSize | 图片大小,可以是数字或百分比 | `number \| string` | `'60%'` | ```jsx import React, { useState } from 'react'; @@ -6,7 +86,7 @@ import { EmptyCanvas } from '@graphscope/studio-components'; export default () => { return (
- +
); }; From 5be90a669333670a0b8869f48975cb39846dc461 Mon Sep 17 00:00:00 2001 From: pomelo-nwu Date: Wed, 16 Apr 2025 15:35:28 +0800 Subject: [PATCH 05/14] feat(studio-components): simplify EmptyCanvas component implementation --- .../src/EmptyCanvas/index.tsx | 45 ++++++++++++++----- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/packages/studio-components/src/EmptyCanvas/index.tsx b/packages/studio-components/src/EmptyCanvas/index.tsx index 24568618b..0be42acde 100644 --- a/packages/studio-components/src/EmptyCanvas/index.tsx +++ b/packages/studio-components/src/EmptyCanvas/index.tsx @@ -1,13 +1,32 @@ -import * as React from 'react'; +import React from 'react'; import { Typography, theme, Flex } from 'antd'; import Image from './image'; -interface IEmptyProps { +/** + * EmptyCanvas 组件的属性接口 + */ +export interface IEmptyCanvasProps { + /** 空状态描述文本 */ description?: string | React.ReactNode; + /** 自定义样式 */ + style?: React.CSSProperties; + /** 自定义类名 */ + className?: string; + /** 图片大小,可以是数字或百分比 */ + imageSize?: number | string; } -const Empty: React.FunctionComponent = props => { - const { description } = props; +/** + * 空画布组件 + * @description 用于展示空状态的组件,支持自定义描述文本和样式 + */ +const EmptyCanvas: React.FC = ({ + description = '暂无数据', + style = {}, + className = '', + imageSize = '60%', +}) => { + const { token } = theme.useToken(); return ( = props => { align="center" justify="center" style={{ - fontSize: '14px', + fontSize: token.fontSize, height: '100%', width: '100%', + position: 'relative', + ...style, }} + className={className} > {description} @@ -39,4 +64,4 @@ const Empty: React.FunctionComponent = props => { ); }; -export default Empty; +export default EmptyCanvas; From 8c1aa5542a9ad05c631df25811edfbbc37bba606 Mon Sep 17 00:00:00 2001 From: pomelo-nwu Date: Wed, 16 Apr 2025 15:40:53 +0800 Subject: [PATCH 06/14] feat(studio-components): optimize SegmentedTabs component and add documentation --- .../src/SegmentedTabs/index.en-US.md | 115 +++++++++++++ .../src/SegmentedTabs/index.md | 116 ++++++++++++- .../src/SegmentedTabs/index.tsx | 159 ++++++++++-------- 3 files changed, 323 insertions(+), 67 deletions(-) create mode 100644 packages/studio-components/src/SegmentedTabs/index.en-US.md diff --git a/packages/studio-components/src/SegmentedTabs/index.en-US.md b/packages/studio-components/src/SegmentedTabs/index.en-US.md new file mode 100644 index 000000000..bc8c0cac9 --- /dev/null +++ b/packages/studio-components/src/SegmentedTabs/index.en-US.md @@ -0,0 +1,115 @@ +--- +title: SegmentedTabs +group: + title: Navigation + order: 1 +--- + +# SegmentedTabs + +A segmented tabs component based on Ant Design Segmented, supporting URL parameter synchronization and custom styles. + +## When To Use + +- When you need to display multiple related content in a limited space +- When you need to categorize content +- When you need tabs that synchronize with URL parameters + +## Examples + +### Basic Usage + +```tsx +import React from 'react'; +import { SegmentedTabs } from '@graphscope/studio-components'; + +const Demo = () => { + const items = [ + { + key: 'tab1', + label: 'Tab One', + children:
Content of Tab One
, + }, + { + key: 'tab2', + label: 'Tab Two', + children:
Content of Tab Two
, + }, + ]; + + return ; +}; + +export default Demo; +``` + +### With Icons + +```tsx +import React from 'react'; +import { SegmentedTabs } from '@graphscope/studio-components'; +import { HomeOutlined, UserOutlined } from '@ant-design/icons'; + +const Demo = () => { + const items = [ + { + key: 'tab1', + label: 'Tab One', + icon: , + children:
Content of Tab One
, + }, + { + key: 'tab2', + label: 'Tab Two', + icon: , + children:
Content of Tab Two
, + }, + ]; + + return ; +}; + +export default Demo; +``` + +### Block Display + +```tsx +import React from 'react'; +import { SegmentedTabs } from '@graphscope/studio-components'; + +const Demo = () => { + const items = [ + { + key: 'tab1', + label: 'Tab One', + children:
Content of Tab One
, + }, + { + key: 'tab2', + label: 'Tab Two', + children:
Content of Tab Two
, + }, + ]; + + return ; +}; + +export default Demo; +``` + +## API + +### SegmentedTabs + +| Property | Description | Type | Default | +| ------------- | ------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ------- | +| items | Tab item configuration | `{ key: string; children: React.ReactNode; label?: React.ReactNode; icon?: React.ReactNode }[]` | - | +| queryKey | URL query parameter key for saving the active tab | `string` | `'tab'` | +| rootStyle | Custom style for the root container | `React.CSSProperties` | - | +| tabStyle | Custom style for the tabs | `React.CSSProperties` | - | +| tableHeight | Height of the tabs | `number` | `40` | +| defaultActive | Default active tab | `string` | - | +| block | Whether to display as block | `boolean` | - | +| value | Current value in controlled mode | `string` | - | +| onChange | Callback when tab changes | `(value: string) => void` | - | diff --git a/packages/studio-components/src/SegmentedTabs/index.md b/packages/studio-components/src/SegmentedTabs/index.md index 521d819d4..b3f834004 100644 --- a/packages/studio-components/src/SegmentedTabs/index.md +++ b/packages/studio-components/src/SegmentedTabs/index.md @@ -1,4 +1,118 @@ -# SegmentedTabs +--- +title: SegmentedTabs 分段式选项卡 +group: + title: 导航 + order: 1 +--- + +# SegmentedTabs 分段式选项卡 + +基于 Ant Design Segmented 组件的选项卡,支持 URL 参数同步和自定义样式。 + +## 何时使用 + +- 需要在有限空间内展示多个相关内容时 +- 需要将内容分类展示时 +- 需要与 URL 参数同步的选项卡时 + +## 代码演示 + +### 基础用法 + +```tsx +import React from 'react'; +import { SegmentedTabs } from '@graphscope/studio-components'; + +const Demo = () => { + const items = [ + { + key: 'tab1', + label: '选项卡一', + children:
选项卡一的内容
, + }, + { + key: 'tab2', + label: '选项卡二', + children:
选项卡二的内容
, + }, + ]; + + return ; +}; + +export default Demo; +``` + +### 带图标 + +```tsx +import React from 'react'; +import { SegmentedTabs } from '@graphscope/studio-components'; +import { HomeOutlined, UserOutlined } from '@ant-design/icons'; + +const Demo = () => { + const items = [ + { + key: 'tab1', + label: '选项卡一', + icon: , + children:
选项卡一的内容
, + }, + { + key: 'tab2', + label: '选项卡二', + icon: , + children:
选项卡二的内容
, + }, + ]; + + return ; +}; + +export default Demo; +``` + +### 块级显示 + +```tsx +import React from 'react'; +import { SegmentedTabs } from '@graphscope/studio-components'; + +const Demo = () => { + const items = [ + { + key: 'tab1', + label: '选项卡一', + children:
选项卡一的内容
, + }, + { + key: 'tab2', + label: '选项卡二', + children:
选项卡二的内容
, + }, + ]; + + return ; +}; + +export default Demo; +``` + +## API + +### SegmentedTabs + +| 参数 | 说明 | 类型 | 默认值 | +| ------------- | ------------------------------------------ | ----------------------------------------------------------------------------------------------- | ------- | +| items | 选项卡项配置 | `{ key: string; children: React.ReactNode; label?: React.ReactNode; icon?: React.ReactNode }[]` | - | +| queryKey | URL 查询参数键名,用于保存当前激活的选项卡 | `string` | `'tab'` | +| rootStyle | 根容器自定义样式 | `React.CSSProperties` | - | +| tabStyle | 选项卡自定义样式 | `React.CSSProperties` | - | +| tableHeight | 选项卡高度 | `number` | `40` | +| defaultActive | 默认激活的选项卡 | `string` | - | +| block | 是否块级显示 | `boolean` | - | +| value | 受控模式下的当前值 | `string` | - | +| onChange | 选项卡切换回调 | `(value: string) => void` | - | ```jsx import React, { useState } from 'react'; diff --git a/packages/studio-components/src/SegmentedTabs/index.tsx b/packages/studio-components/src/SegmentedTabs/index.tsx index eb97663ea..846ead3b3 100644 --- a/packages/studio-components/src/SegmentedTabs/index.tsx +++ b/packages/studio-components/src/SegmentedTabs/index.tsx @@ -1,88 +1,115 @@ +import React, { useMemo, useCallback } from 'react'; import { Segmented, theme } from 'antd'; -import * as React from 'react'; -import { ReactNode } from 'react'; import { getSearchParams, setSearchParams } from '../Utils'; +/** + * SegmentedTabs 组件的属性接口 + */ export interface SegmentedTabsProps { - items: { key: string; children: ReactNode; label?: React.ReactNode; icon?: ReactNode }[]; + /** 选项卡项配置 */ + items: { + /** 选项卡的唯一标识 */ + key: string; + /** 选项卡内容 */ + children: React.ReactNode; + /** 选项卡标签 */ + label?: React.ReactNode; + /** 选项卡图标 */ + icon?: React.ReactNode; + }[]; + /** URL 查询参数键名,用于保存当前激活的选项卡 */ queryKey?: string; + /** 根容器自定义样式 */ rootStyle?: React.CSSProperties; + /** 选项卡自定义样式 */ tabStyle?: React.CSSProperties; + /** 选项卡高度 */ tableHeight?: number; + /** 默认激活的选项卡 */ defaultActive?: string; + /** 是否块级显示 */ block?: boolean; + /** 受控模式下的当前值 */ value?: string; + /** 选项卡切换回调 */ onChange?: (value: string) => void; } -const SegmentedTabs: React.FunctionComponent = props => { - const { - items, - tableHeight = 40, - queryKey = 'tab', - rootStyle = {}, - tabStyle = {}, - defaultActive, - block, - value, - onChange, - } = props; - //@ts-ignore - const [state, setState] = React.useState<{ active: string }>(() => { +/** + * 分段式选项卡组件 + * @description 基于 Ant Design Segmented 组件的选项卡,支持 URL 参数同步和自定义样式 + */ +const SegmentedTabs: React.FC = ({ + items, + tableHeight = 40, + queryKey = 'tab', + rootStyle = {}, + tabStyle = {}, + defaultActive, + block, + value, + onChange, +}) => { + const { token } = theme.useToken(); + + // 初始化状态,从 URL 参数或默认值获取当前激活的选项卡 + const [activeTab, setActiveTab] = React.useState(() => { const defaultKey = getSearchParams(queryKey); - const active = defaultKey || defaultActive || items[0]?.key || ''; - return { - active, - }; + return defaultKey || defaultActive || items[0]?.key || ''; }); - const styles: Record = { - tabs: { - height: '100%', - padding: '12px 0px', - boxSizing: 'border-box', - }, - appear: { - display: 'block', - padding: '0px 6px', - height: `calc(100% - ${tableHeight}px)`, - }, - hidden: { - display: 'none', - padding: '0px 6px', - height: `calc(100% - ${tableHeight}px)`, - }, - }; + // 使用 useMemo 优化样式计算 + const styles = useMemo>( + () => ({ + tabs: { + height: '100%', + padding: `${token.padding}px 0px`, + boxSizing: 'border-box', + }, + appear: { + display: 'block', + padding: `0px ${token.paddingXS}px`, + height: `calc(100% - ${tableHeight}px)`, + }, + hidden: { + display: 'none', + padding: `0px ${token.paddingXS}px`, + height: `calc(100% - ${tableHeight}px)`, + }, + }), + [token.padding, token.paddingXS, tableHeight], + ); - const { active } = state; + // 使用 useMemo 优化选项计算 + const options = useMemo( + () => + items.map(item => ({ + value: item.key, + label: item.label, + icon: item.icon, + })), + [items], + ); - const options = items.map(item => { - return { - value: item.key, - label: item.label, - icon: item.icon, - }; - }); + // 使用 useCallback 优化事件处理函数 + const handleChange = useCallback( + (newValue: string) => { + if (onChange) { + onChange(newValue); + return; + } - const handleChange = value => { - if (onChange) { - onChange(value); - return; - } + setSearchParams({ + [queryKey]: newValue, + }); - setSearchParams({ - [queryKey]: value, - }); + setActiveTab(newValue); + }, + [onChange, queryKey], + ); - setState(preState => { - return { - ...preState, - active: value, - }; - }); - }; - const val = value || active; - const { token } = theme.useToken(); + // 当前激活的选项卡值 + const currentValue = value || activeTab; return (
= props => { > <> {items.map(item => { const { key, children } = item; - const isActive = key === val; + const isActive = key === currentValue; return (
{children} From c5c7ea128de6339a2180d5d911f9cd36f3283001 Mon Sep 17 00:00:00 2001 From: pomelo-nwu Date: Wed, 16 Apr 2025 15:46:30 +0800 Subject: [PATCH 07/14] feat(studio-components): add loadingText prop to TypingText component --- .../src/TypingText/index.en-US.md | 108 +++++++++++++++++ .../studio-components/src/TypingText/index.md | 109 ++++++++++++++++-- .../src/TypingText/index.tsx | 67 ++++++++--- 3 files changed, 263 insertions(+), 21 deletions(-) create mode 100644 packages/studio-components/src/TypingText/index.en-US.md diff --git a/packages/studio-components/src/TypingText/index.en-US.md b/packages/studio-components/src/TypingText/index.en-US.md new file mode 100644 index 000000000..f2080a389 --- /dev/null +++ b/packages/studio-components/src/TypingText/index.en-US.md @@ -0,0 +1,108 @@ +--- +title: TypingText +group: + title: Feedback + order: 1 +--- + +# TypingText + +A typing effect text component based on typewriter-effect, supporting custom typing speed, deletion speed, and loop playback. + +## When To Use + +- When you need to display a typing effect +- When you need to emphasize text content +- When you need to show loading or generation process + +## Examples + +### Basic Usage + +```tsx +import React from 'react'; +import { TypingText } from '@graphscope/studio-components'; + +const Demo = () => { + return This is a typing effect text; +}; + +export default Demo; +``` + +### Custom Speed + +```tsx +import React from 'react'; +import { TypingText } from '@graphscope/studio-components'; + +const Demo = () => { + return ( + + This is a typing effect text with custom speed + + ); +}; + +export default Demo; +``` + +### Loop Playback + +```tsx +import React from 'react'; +import { TypingText } from '@graphscope/studio-components'; + +const Demo = () => { + return This is a typing effect text with loop playback; +}; + +export default Demo; +``` + +### Completion Callback + +```tsx +import React from 'react'; +import { TypingText } from '@graphscope/studio-components'; + +const Demo = () => { + const handleComplete = () => { + console.log('Typing effect completed'); + }; + + return This is a typing effect text with completion callback; +}; + +export default Demo; +``` + +### Custom Loading Text + +```tsx +import React from 'react'; +import { TypingText } from '@graphscope/studio-components'; + +const Demo = () => { + return This is a typing effect text with custom loading text; +}; + +export default Demo; +``` + +## API + +### TypingText + +| Property | Description | Type | Default | +| ----------- | ----------------------------------------- | ------------ | ----------------- | +| children | Text content to display | `string` | - | +| delay | Typing delay time (ms) | `number` | `10` | +| deleteSpeed | Deletion speed (ms) | `number` | `10` | +| loop | Whether to loop playback | `boolean` | `false` | +| onComplete | Callback function when typing is complete | `() => void` | - | +| loadingText | Loading text to display | `string` | `'Generating...'` | + +``` + +``` diff --git a/packages/studio-components/src/TypingText/index.md b/packages/studio-components/src/TypingText/index.md index 519da4ff2..196046dea 100644 --- a/packages/studio-components/src/TypingText/index.md +++ b/packages/studio-components/src/TypingText/index.md @@ -1,15 +1,108 @@ -```jsx -import React, { useState } from 'react'; -import { Space, Button } from 'antd'; +--- +title: TypingText 打字机效果文本 +group: + title: 反馈 + order: 1 +--- + +# TypingText 打字机效果文本 + +一个基于 typewriter-effect 实现的打字机效果文本组件,支持自定义打字速度、删除速度和循环播放。 + +## 何时使用 + +- 需要展示打字机效果时 +- 需要强调文本内容时 +- 需要展示加载或生成过程时 + +## 代码演示 + +### 基础用法 + +```tsx +import React from 'react'; +import { TypingText } from '@graphscope/studio-components'; + +const Demo = () => { + return 这是一段打字机效果文本; +}; + +export default Demo; +``` + +### 自定义速度 + +```tsx +import React from 'react'; import { TypingText } from '@graphscope/studio-components'; -export default () => { +const Demo = () => { return ( - - GraphScope Portal is a user-friendly web interface that simplifies managing graph data with GraphScope. It offers - one-stop access to data modeling, importing, querying, and monitoring, catering to both Interactive and Insight - engines within the GraphScope Flex architecture. + + 这是一段自定义速度的打字机效果文本 ); }; + +export default Demo; +``` + +### 循环播放 + +```tsx +import React from 'react'; +import { TypingText } from '@graphscope/studio-components'; + +const Demo = () => { + return 这是一段循环播放的打字机效果文本; +}; + +export default Demo; +``` + +### 完成回调 + +```tsx +import React from 'react'; +import { TypingText } from '@graphscope/studio-components'; + +const Demo = () => { + const handleComplete = () => { + console.log('打字效果完成'); + }; + + return 这是一段带有完成回调的打字机效果文本; +}; + +export default Demo; +``` + +### 自定义加载提示 + +```tsx +import React from 'react'; +import { TypingText } from '@graphscope/studio-components'; + +const Demo = () => { + return 这是一段带有自定义加载提示的打字机效果文本; +}; + +export default Demo; +``` + +## API + +### TypingText + +| 参数 | 说明 | 类型 | 默认值 | +| ----------- | -------------------- | ------------ | ----------------- | +| children | 要显示的文本内容 | `string` | - | +| delay | 打字延迟时间(毫秒) | `number` | `10` | +| deleteSpeed | 删除速度(毫秒) | `number` | `10` | +| loop | 是否循环播放 | `boolean` | `false` | +| onComplete | 打字完成后的回调函数 | `() => void` | - | +| loadingText | 加载提示文本 | `string` | `'Generating...'` | + +``` + ``` diff --git a/packages/studio-components/src/TypingText/index.tsx b/packages/studio-components/src/TypingText/index.tsx index b72b19048..5f1d4f0c3 100644 --- a/packages/studio-components/src/TypingText/index.tsx +++ b/packages/studio-components/src/TypingText/index.tsx @@ -1,28 +1,69 @@ import * as React from 'react'; import Typewriter from 'typewriter-effect'; -interface ITypingTextProps { +import { useCallback } from 'react'; + +/** + * TypingText 组件的属性接口 + */ +export interface ITypingTextProps { + /** 要显示的文本内容 */ children: string; + /** 打字延迟时间(毫秒) */ + delay?: number; + /** 删除速度(毫秒) */ + deleteSpeed?: number; + /** 是否循环播放 */ + loop?: boolean; + /** 打字完成后的回调函数 */ + onComplete?: () => void; + /** 加载提示文本 */ + loadingText?: string; } -const TypingText: React.FunctionComponent = ({ children }) => { - return ( - { +/** + * 打字机效果文本组件 + * @description 使用 typewriter-effect 实现的打字机效果文本组件 + */ +const TypingText: React.FC = ({ + children, + delay = 10, + deleteSpeed = 10, + loop = false, + onComplete, + loadingText = 'Generating...', +}) => { + // 使用 useCallback 优化初始化函数 + const handleInit = useCallback( + (typewriter: any) => { + if (loop) { + // 循环播放时,直接显示文本,不显示加载提示 + typewriter.typeString(children).pauseFor(1000).deleteAll().start(); + } else { + // 非循环播放时,显示加载提示然后显示实际文本 typewriter - .typeString('Generating...') + .typeString(loadingText) .pauseFor(100) - .deleteChars(14) + .deleteChars(loadingText.length) .typeString(children) .start() - .callFunction(function (state) { + .callFunction(function (state: any) { state.elements.cursor.style.display = 'none'; + onComplete?.(); }); + } + }, + [children, loop, onComplete, loadingText], + ); + + return ( + ); }; From 98e657373ce458cd74e1a17e0688380c5c4e4170 Mon Sep 17 00:00:00 2001 From: pomelo-nwu Date: Wed, 16 Apr 2025 15:58:31 +0800 Subject: [PATCH 08/14] refactor(studio-components): remove relative prop from Toolbar component --- .../src/Toolbar/index.en-US.md | 124 ++++++++++++++++++ .../studio-components/src/Toolbar/index.md | 124 ++++++++++++++++-- .../studio-components/src/Toolbar/index.tsx | 81 ++++++++---- 3 files changed, 296 insertions(+), 33 deletions(-) create mode 100644 packages/studio-components/src/Toolbar/index.en-US.md diff --git a/packages/studio-components/src/Toolbar/index.en-US.md b/packages/studio-components/src/Toolbar/index.en-US.md new file mode 100644 index 000000000..d9ce884ca --- /dev/null +++ b/packages/studio-components/src/Toolbar/index.en-US.md @@ -0,0 +1,124 @@ +--- +title: Toolbar +group: + title: Navigation + order: 2 +--- + +# Toolbar + +A customizable toolbar component that supports horizontal and vertical arrangements, with configurable position and styles. + +## When To Use + +- When you need to display a group of action buttons in a fixed position +- When you need to customize toolbar styles and position +- When you need to control toolbar display effects + +## Examples + +### Basic Usage + +```tsx +import React from 'react'; +import { Button } from 'antd'; +import { Toolbar } from '@graphscope/studio-components'; +import { PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons'; + +const Demo = () => { + return ( +
+ +
+ ); +}; + +export default Demo; +``` + +### Horizontal Arrangement + +```tsx +import React from 'react'; +import { Button } from 'antd'; +import { Toolbar } from '@graphscope/studio-components'; +import { PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons'; + +const Demo = () => { + return ( +
+ +
+ ); +}; + +export default Demo; +``` + +### Custom Position + +```tsx +import React from 'react'; +import { Button } from 'antd'; +import { Toolbar } from '@graphscope/studio-components'; +import { PlusOutlined, EditOutlined } from '@ant-design/icons'; + +const Demo = () => { + return ( +
+ +
+ ); +}; + +export default Demo; +``` + +### Custom Styles + +```tsx +import React from 'react'; +import { Button } from 'antd'; +import { Toolbar } from '@graphscope/studio-components'; +import { PlusOutlined, EditOutlined } from '@ant-design/icons'; + +const Demo = () => { + return ( +
+ +
+ ); +}; + +export default Demo; +``` + +## API + +### Toolbar + +| Property | Description | Type | Default | +| ---------- | ------------------------------- | ----------------------------------------------------------------------------------------------------------- | ------------------------------- | +| children | Toolbar content | `React.ReactNode` | - | +| style | Custom style | `React.CSSProperties` | - | +| direction | Arrangement direction | `'horizontal' \| 'vertical'` | `'vertical'` | +| noSpace | Whether to disable spacing | `boolean` | `false` | +| position | Toolbar position | `{ top?: string \| number; left?: string \| number; right?: string \| number; bottom?: string \| number; }` | `{ top: '12px', left: '24px' }` | +| shadow | Whether to show shadow | `boolean` | `true` | +| background | Whether to show background | `boolean` | `true` | +| rounded | Whether to show rounded corners | `boolean` | `true` | +| padding | Padding | `string \| number` | `'4px'` | diff --git a/packages/studio-components/src/Toolbar/index.md b/packages/studio-components/src/Toolbar/index.md index 6adbb36ad..171d00721 100644 --- a/packages/studio-components/src/Toolbar/index.md +++ b/packages/studio-components/src/Toolbar/index.md @@ -1,18 +1,124 @@ -# Toolbar +--- +title: Toolbar 工具栏 +group: + title: 导航 + order: 2 +--- -```jsx -import React, { useState } from 'react'; -import { Space, Button } from 'antd'; -import { Toolbar, Icons } from '@graphscope/studio-components'; +# Toolbar 工具栏 -export default () => { +一个可自定义的工具栏组件,支持水平和垂直排列,可配置位置、样式等。 + +## 何时使用 + +- 需要在一个固定位置展示一组操作按钮时 +- 需要自定义工具栏样式和位置时 +- 需要控制工具栏的显示效果时 + +## 代码演示 + +### 基础用法 + +```tsx +import React from 'react'; +import { Button } from 'antd'; +import { Toolbar } from '@graphscope/studio-components'; +import { PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons'; + +const Demo = () => { return ( -
+
-
+ ); +}; + +export default Demo; +``` + +### 水平排列 + +```tsx +import React from 'react'; +import { Button } from 'antd'; +import { Toolbar } from '@graphscope/studio-components'; +import { PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons'; + +const Demo = () => { + return ( +
+ +
+ ); +}; + +export default Demo; +``` + +### 自定义位置 + +```tsx +import React from 'react'; +import { Button } from 'antd'; +import { Toolbar } from '@graphscope/studio-components'; +import { PlusOutlined, EditOutlined } from '@ant-design/icons'; + +const Demo = () => { + return ( +
+ +
+ ); +}; + +export default Demo; +``` + +### 自定义样式 + +```tsx +import React from 'react'; +import { Button } from 'antd'; +import { Toolbar } from '@graphscope/studio-components'; +import { PlusOutlined, EditOutlined } from '@ant-design/icons'; + +const Demo = () => { + return ( +
+ +
); }; + +export default Demo; ``` + +## API + +### Toolbar + +| 参数 | 说明 | 类型 | 默认值 | +| ---------- | -------------- | ----------------------------------------------------------------------------------------------------------- | ------------------------------- | +| children | 工具栏内容 | `React.ReactNode` | - | +| style | 自定义样式 | `React.CSSProperties` | - | +| direction | 排列方向 | `'horizontal' \| 'vertical'` | `'vertical'` | +| noSpace | 是否禁用间距 | `boolean` | `false` | +| position | 工具栏位置 | `{ top?: string \| number; left?: string \| number; right?: string \| number; bottom?: string \| number; }` | `{ top: '12px', left: '24px' }` | +| shadow | 是否显示阴影 | `boolean` | `true` | +| background | 是否显示背景色 | `boolean` | `true` | +| rounded | 是否显示圆角 | `boolean` | `true` | +| padding | 内边距 | `string \| number` | `'4px'` | diff --git a/packages/studio-components/src/Toolbar/index.tsx b/packages/studio-components/src/Toolbar/index.tsx index 6f7fca058..9709a187d 100644 --- a/packages/studio-components/src/Toolbar/index.tsx +++ b/packages/studio-components/src/Toolbar/index.tsx @@ -1,36 +1,69 @@ import * as React from 'react'; - import { Space, theme } from 'antd'; -interface IToolbarProps { + +/** + * Toolbar 组件的属性接口 + */ +export interface IToolbarProps { + /** 工具栏内容 */ children: React.ReactNode; + /** 自定义样式 */ style?: React.CSSProperties; + /** 排列方向 */ direction?: 'horizontal' | 'vertical'; + /** 是否禁用间距 */ noSpace?: boolean; + /** 工具栏位置 */ + position?: { + top?: string | number; + left?: string | number; + right?: string | number; + bottom?: string | number; + }; + /** 是否显示阴影 */ + shadow?: boolean; + /** 是否显示背景色 */ + background?: boolean; + /** 是否显示圆角 */ + rounded?: boolean; + /** 内边距 */ + padding?: string | number; } -const Toolbar: React.FunctionComponent = props => { - const { children, style, direction = 'vertical', noSpace } = props; +/** + * 工具栏组件 + * @description 一个可自定义的工具栏组件,支持水平和垂直排列,可配置位置、样式等 + */ +const Toolbar: React.FC = ({ + children, + style, + direction = 'vertical', + noSpace = false, + position = { top: '12px', left: '24px' }, + shadow = true, + background = true, + rounded = true, + padding = '4px', +}) => { const { token } = theme.useToken(); - const _children = noSpace ? children : {children}; - return ( -
- {_children} -
- ); + + const containerStyle: React.CSSProperties = { + boxShadow: shadow ? token.boxShadow : 'none', + borderRadius: rounded ? token.borderRadius : 0, + background: background ? token.colorBgContainer : 'transparent', + display: 'flex', + flexDirection: 'column', + position: 'absolute', + top: position.top, + left: position.left, + right: position.right, + bottom: position.bottom, + zIndex: 999, + padding, + ...style, + }; + + return
{noSpace ? children : {children}}
; }; export default Toolbar; From 2c3c39d6c41b728a44660c1f434a64c112e9c6d2 Mon Sep 17 00:00:00 2001 From: pomelo-nwu Date: Wed, 16 Apr 2025 17:08:18 +0800 Subject: [PATCH 09/14] feat(studio-components): update Illustration component documentation --- .../src/Illustration/index.en-US.md | 111 ++++++++++++++++++ .../src/Illustration/index.md | 94 ++++++++++++++- .../src/Illustration/index.tsx | 19 ++- 3 files changed, 217 insertions(+), 7 deletions(-) create mode 100644 packages/studio-components/src/Illustration/index.en-US.md diff --git a/packages/studio-components/src/Illustration/index.en-US.md b/packages/studio-components/src/Illustration/index.en-US.md new file mode 100644 index 000000000..07a5f8f3f --- /dev/null +++ b/packages/studio-components/src/Illustration/index.en-US.md @@ -0,0 +1,111 @@ +--- +title: Illustration +group: + title: Data Display + order: 3 +--- + +# Illustration + +A collection of beautiful illustrations to enhance the visual effects and user experience of your interface. All illustrations are sourced from [unDraw](https://undraw.co/illustrations) and optimized for use in the component library. + +## When To Use + +- When you need to display friendly visual cues for empty states, loading states, etc. +- When you want to add vivid visual elements to your interface +- When you need to manage illustration resources in your project + +## Examples + +### Basic Usage + +```tsx +import React from 'react'; +import { Illustration } from '@graphscope/studio-components'; + +const Demo = () => { + return ; +}; + +export default Demo; +``` + +### Custom Color + +```tsx +import React from 'react'; +import { Illustration } from '@graphscope/studio-components'; + +const Demo = () => { + return ; +}; + +export default Demo; +``` + +### Custom Size + +```tsx +import React from 'react'; +import { Illustration } from '@graphscope/studio-components'; + +const Demo = () => { + return ; +}; + +export default Demo; +``` + +### Illustration List + +```tsx +import React from 'react'; +import { Space, Flex, Typography } from 'antd'; +import { Illustration } from '@graphscope/studio-components'; + +const Demo = () => { + return ( + + {Object.keys(Illustration).map(key => { + const Item = Illustration[key]; + return ( + + + {key} + + ); + })} + + ); +}; + +export default Demo; +``` + +## API + +### Illustration + +| Name | Description | Usage Scenarios | +| ------------ | -------------------------- | -------------------------------------- | +| Welcome | Welcome illustration | Welcome page, homepage | +| Job | Job illustration | Task list, workspace | +| Explore | Explore illustration | Data exploration, analysis page | +| DesignSchema | Design schema illustration | Design page, pattern display | +| Process | Process illustration | Process display, step description | +| Success | Success illustration | Operation success, completion status | +| Next | Next step illustration | Guide process, step prompt | +| Loading | Loading illustration | Loading state, waiting prompt | +| Upload | Upload illustration | File upload, import function | +| FunArrow | Fun arrow illustration | Interaction prompt, guide description | +| Programming | Programming illustration | Code related, development function | +| Experiment | Experiment illustration | Experiment function, test scenario | +| Settings | Settings illustration | Settings page, configuration function | +| Charts | Charts illustration | Data visualization, statistics display | + +### IIllustrationProps + +| Property | Description | Type | Default | +| --------- | ----------------- | --------------------- | ------- | +| style | Custom styles | `React.CSSProperties` | - | +| className | Custom class name | `string` | - | diff --git a/packages/studio-components/src/Illustration/index.md b/packages/studio-components/src/Illustration/index.md index 00086c532..c89868455 100644 --- a/packages/studio-components/src/Illustration/index.md +++ b/packages/studio-components/src/Illustration/index.md @@ -1,17 +1,43 @@ --- -tag: New +title: Illustration 插画 +group: + title: 数据展示 + order: 3 --- -# Illustration +# Illustration 插画 -- open source : https://undraw.co/illustrations +一套精美的插画组件,用于增强用户界面的视觉效果和用户体验。所有插画均来自 [unDraw](https://undraw.co/illustrations),并经过优化以适应组件库的使用场景。 -```jsx -import React, { useState } from 'react'; +## 何时使用 + +- 需要在空状态、加载状态等场景下展示友好的视觉提示时 +- 需要为界面添加生动的视觉元素时 +- 需要统一管理项目中的插画资源时 + +## Examples + +### Basic Usage + +```tsx +import React from 'react'; +import { Illustration } from '@graphscope/studio-components'; + +const Demo = () => { + return ; +}; + +export default Demo; +``` + +### Illustration List + +```tsx +import React from 'react'; import { Space, Flex, Typography } from 'antd'; import { Illustration } from '@graphscope/studio-components'; -export default () => { +const Demo = () => { return ( {Object.keys(Illustration).map(key => { @@ -26,4 +52,60 @@ export default () => { ); }; + +export default Demo; ``` + +### Custom Color + +```tsx +import React from 'react'; +import { Illustration } from '@graphscope/studio-components'; + +const Demo = () => { + return ; +}; + +export default Demo; +``` + +### Custom Size + +```tsx +import React from 'react'; +import { Illustration } from '@graphscope/studio-components'; + +const Demo = () => { + return ; +}; + +export default Demo; +``` + +## API + +### Illustration + +| 插画名称 | 说明 | 使用场景 | +| ------------ | ------------ | -------------------- | +| Welcome | 欢迎插画 | 欢迎页面、首页 | +| Job | 工作插画 | 任务列表、工作台 | +| Explore | 探索插画 | 数据探索、分析页面 | +| DesignSchema | 设计模式插画 | 设计页面、模式展示 | +| Process | 流程插画 | 流程展示、步骤说明 | +| Success | 成功插画 | 操作成功、完成状态 | +| Next | 下一步插画 | 引导流程、步骤提示 | +| Loading | 加载插画 | 加载状态、等待提示 | +| Upload | 上传插画 | 文件上传、导入功能 | +| FunArrow | 趣味箭头插画 | 交互提示、引导说明 | +| Programming | 编程插画 | 代码相关、开发功能 | +| Experiment | 实验插画 | 实验功能、测试场景 | +| Settings | 设置插画 | 设置页面、配置功能 | +| Charts | 图表插画 | 数据可视化、统计展示 | + +### IIllustrationProps + +| 参数 | 说明 | 类型 | 默认值 | +| --------- | ---------- | --------------------- | ------ | +| style | 自定义样式 | `React.CSSProperties` | - | +| className | 自定义类名 | `string` | - | diff --git a/packages/studio-components/src/Illustration/index.tsx b/packages/studio-components/src/Illustration/index.tsx index fc0f64924..6b39cd517 100644 --- a/packages/studio-components/src/Illustration/index.tsx +++ b/packages/studio-components/src/Illustration/index.tsx @@ -1,3 +1,6 @@ +import * as React from 'react'; + +// 导入所有插画组件 import Job from './Job'; import Explore from './Explore'; import DesignSchema from './DesignSchema'; @@ -12,7 +15,19 @@ import Programming from './Programming'; import Experiment from './Experiment'; import Settings from './Settings'; import Charts from './Charts'; -export default { + +/** + * 插画组件的通用属性接口 + */ +export interface IIllustrationProps { + /** 自定义样式 */ + style?: React.CSSProperties; + /** 自定义类名 */ + className?: string; +} + +// 导出所有插画组件 +const Illustration = { Job, Explore, DesignSchema, @@ -28,3 +43,5 @@ export default { Settings, Charts, }; + +export default Illustration; From 7340bfb6034f31b155f3b40ad0e6bf9d3b14b694 Mon Sep 17 00:00:00 2001 From: pomelo-nwu Date: Wed, 16 Apr 2025 17:20:26 +0800 Subject: [PATCH 10/14] feat(studio-components): update ResizablePanel documentation and fix import path --- .../src/ResizablePanel/index.en-US.md | 56 +++++ .../src/ResizablePanel/index.md | 100 +++++++- .../src/ResizablePanel/index.tsx | 215 +++++++++++++----- 3 files changed, 316 insertions(+), 55 deletions(-) create mode 100644 packages/studio-components/src/ResizablePanel/index.en-US.md diff --git a/packages/studio-components/src/ResizablePanel/index.en-US.md b/packages/studio-components/src/ResizablePanel/index.en-US.md new file mode 100644 index 000000000..bddf5d764 --- /dev/null +++ b/packages/studio-components/src/ResizablePanel/index.en-US.md @@ -0,0 +1,56 @@ +--- +title: ResizablePanel +group: + title: Layout + order: 2 +--- + +# ResizablePanel + +## Component Introduction + +ResizablePanel is a resizable panel component that supports left, middle, and right panel areas. Users can adjust the width of each panel by dragging the handle. + +## Usage Scenarios + +- When a resizable panel layout is needed +- When flexible panel width control is required + +## Code Demo + +```jsx +import React from 'react'; +import { ResizablePanel } from '@graphscope/studio-components'; + +const Demo = () => { + return ( + Left Panel Content
} + middlePanel={
Middle Panel Content
} + rightPanel={
Right Panel Content
} + leftMinWidth={20} + leftMaxWidth={40} + rightMinWidth={20} + rightMaxWidth={40} + /> + ); +}; + +export default Demo; +``` + +## API + +### ResizablePanel + +| Property | Description | Type | Default | +| ------------- | --------------------------------------- | --------------------- | ------- | +| leftPanel | Left panel content | `React.ReactNode` | - | +| middlePanel | Middle panel content | `React.ReactNode` | - | +| rightPanel | Right panel content | `React.ReactNode` | - | +| leftMinWidth | Minimum width percentage of left panel | `number` | 20 | +| leftMaxWidth | Maximum width percentage of left panel | `number` | 40 | +| rightMinWidth | Minimum width percentage of right panel | `number` | 20 | +| rightMaxWidth | Maximum width percentage of right panel | `number` | 40 | +| style | Custom styles | `React.CSSProperties` | - | +| className | Custom class name | `string` | - | diff --git a/packages/studio-components/src/ResizablePanel/index.md b/packages/studio-components/src/ResizablePanel/index.md index 3bc3f7be9..09aad7fa8 100644 --- a/packages/studio-components/src/ResizablePanel/index.md +++ b/packages/studio-components/src/ResizablePanel/index.md @@ -1,7 +1,105 @@ --- -title: ResizablePanels +title: ResizablePanel 可调整大小面板 +group: + title: 布局 + order: 2 --- +# ResizablePanel 可调整大小面板 + +## 组件介绍 + +ResizablePanel 是一个可调整大小的面板组件,支持左侧、中间和右侧三个面板区域。用户可以通过拖拽手柄来调整各个面板的宽度。 + +## 使用场景 + +- 需要可调整大小的面板布局 +- 需要灵活的面板宽度控制 + +## 代码演示 + +### 基础用法 + +```tsx +import React from 'react'; +import { ResizablePanel } from '@graphscope/studio-components'; + +const Demo = () => { + return ( +
+ 左侧面板
} + middlePanel={
中间面板
} + rightPanel={
右侧面板
} + /> +
+ ); +}; + +export default Demo; +``` + +### 自定义最小和最大宽度 + +```tsx +import React from 'react'; +import { ResizablePanel } from '@graphscope/studio-components'; + +const Demo = () => { + return ( +
+ 左侧面板
} + middlePanel={
中间面板
} + rightPanel={
右侧面板
} + leftMinWidth={20} + leftMaxWidth={40} + rightMinWidth={20} + rightMaxWidth={40} + /> +
+ ); +}; + +export default Demo; +``` + +### 仅使用两个面板 + +```tsx +import React from 'react'; +import { ResizablePanel } from '@graphscope/studio-components'; + +const Demo = () => { + return ( +
+ 左侧面板
} + middlePanel={
右侧面板
} + /> +
+ ); +}; + +export default Demo; +``` + +## API + +### ResizablePanel + +| 属性 | 说明 | 类型 | 默认值 | +| ------------- | ---------------------- | ------------------- | ------ | +| leftPanel | 左侧面板内容 | React.ReactNode | - | +| middlePanel | 中间面板内容 | React.ReactNode | - | +| rightPanel | 右侧面板内容 | React.ReactNode | - | +| leftMinWidth | 左侧面板最小宽度百分比 | number | 20 | +| leftMaxWidth | 左侧面板最大宽度百分比 | number | 40 | +| rightMinWidth | 右侧面板最小宽度百分比 | number | 20 | +| rightMaxWidth | 右侧面板最大宽度百分比 | number | 40 | +| style | 自定义样式 | React.CSSProperties | - | +| className | 自定义类名 | string | - | + ```jsx import React from 'react'; import ResizablePanel from './index.tsx'; diff --git a/packages/studio-components/src/ResizablePanel/index.tsx b/packages/studio-components/src/ResizablePanel/index.tsx index be2568549..024f2e03e 100644 --- a/packages/studio-components/src/ResizablePanel/index.tsx +++ b/packages/studio-components/src/ResizablePanel/index.tsx @@ -1,65 +1,172 @@ -import React from 'react'; -import { Panel, PanelGroup } from 'react-resizable-panels'; -import ResizeHandle from './ResizeHandle'; - -interface IResizablePanelsProps { - leftSide?: React.ReactNode; - middleSide?: React.ReactNode; - rightSide?: React.ReactNode; - leftMinSize?: number; - leftMaxSize?: number; - rightMinSize?: number; - rightMaxSize?: number; -} +import React, { useState, useRef, useEffect, useMemo, useCallback } from 'react'; -const styles: Record = { - Container: { - width: '100%', - height: '100%', - display: 'flex', - flexDirection: 'column', - gap: '1rem', - }, - Panel: { display: 'flex', flexDirection: 'row' }, - PanelContent: { - height: '100%', - width: '100%', - borderRadius: '0.5rem', - }, -}; +/** + * 可调整大小面板组件的属性接口 + */ +export interface IResizablePanelProps { + /** 左侧面板内容 */ + leftPanel?: React.ReactNode; + /** 中间面板内容 */ + middlePanel?: React.ReactNode; + /** 右侧面板内容 */ + rightPanel?: React.ReactNode; + /** 左侧面板最小宽度百分比 */ + leftMinWidth?: number; + /** 左侧面板最大宽度百分比 */ + leftMaxWidth?: number; + /** 右侧面板最小宽度百分比 */ + rightMinWidth?: number; + /** 右侧面板最大宽度百分比 */ + rightMaxWidth?: number; + /** 自定义样式 */ + style?: React.CSSProperties; + /** 自定义类名 */ + className?: string; +} -const ResizablePanels: React.FC = ({ - leftSide, - middleSide, - rightSide, - leftMinSize = 20, - leftMaxSize = 40, - rightMinSize = 20, - rightMaxSize = 40, +/** + * 可调整大小面板组件 + * + * 一个简单的可调整大小的面板组件,支持左侧、中间和右侧三个面板区域 + */ +const ResizablePanel: React.FC = ({ + leftPanel, + middlePanel, + rightPanel, + leftMinWidth = 20, + leftMaxWidth = 40, + rightMinWidth = 20, + rightMaxWidth = 40, + style, + className, }) => { - const renderLeftPanel = () => ( - -
{leftSide}
-
- ); + // 面板宽度状态 + const [panelState, setPanelState] = useState<{ leftWidth: number; rightWidth: number }>({ + leftWidth: 30, + rightWidth: 30, + }); + + // 拖拽状态 + const [dragState, setDragState] = useState<{ isDraggingLeft: boolean; isDraggingRight: boolean }>({ + isDraggingLeft: false, + isDraggingRight: false, + }); + + // 容器引用 + const containerRef = useRef(null); + + // 处理左侧拖拽 + const handleLeftDragStart = useCallback(() => { + setDragState(prev => ({ ...prev, isDraggingLeft: true })); + }, []); + + // 处理右侧拖拽 + const handleRightDragStart = useCallback(() => { + setDragState(prev => ({ ...prev, isDraggingRight: true })); + }, []); + + // 处理拖拽结束 + const handleDragEnd = useCallback(() => { + setDragState({ isDraggingLeft: false, isDraggingRight: false }); + }, []); + + // 处理拖拽移动 + const handleDragMove = useCallback( + (e: MouseEvent) => { + if (!containerRef.current) return; + + const containerWidth = containerRef.current.offsetWidth; + const containerRect = containerRef.current.getBoundingClientRect(); - const renderRightPanel = () => ( - -
{rightSide}
-
+ if (dragState.isDraggingLeft) { + // 计算左侧面板宽度百分比 + const newLeftWidth = ((e.clientX - containerRect.left) / containerWidth) * 100; + + // 限制在最小和最大宽度之间 + if (newLeftWidth >= leftMinWidth && newLeftWidth <= leftMaxWidth) { + setPanelState(prev => ({ ...prev, leftWidth: newLeftWidth })); + } + } + + if (dragState.isDraggingRight) { + // 计算右侧面板宽度百分比 + const newRightWidth = ((containerRect.right - e.clientX) / containerWidth) * 100; + + // 限制在最小和最大宽度之间 + if (newRightWidth >= rightMinWidth && newRightWidth <= rightMaxWidth) { + setPanelState(prev => ({ ...prev, rightWidth: newRightWidth })); + } + } + }, + [dragState, leftMinWidth, leftMaxWidth, rightMinWidth, rightMaxWidth], ); + // 添加和移除全局事件监听 + useEffect(() => { + if (dragState.isDraggingLeft || dragState.isDraggingRight) { + document.addEventListener('mousemove', handleDragMove); + document.addEventListener('mouseup', handleDragEnd); + } + + return () => { + document.removeEventListener('mousemove', handleDragMove); + document.removeEventListener('mouseup', handleDragEnd); + }; + }, [dragState, handleDragMove, handleDragEnd]); + + // 计算中间面板宽度 + const middleWidth = useMemo(() => 100 - panelState.leftWidth - panelState.rightWidth, [panelState]); + return ( -
- - {leftSide && [renderLeftPanel(), ]} - -
{middleSide}
-
- {rightSide && [, renderRightPanel()]} -
+
+ {/* 左侧面板 */} + {leftPanel &&
{leftPanel}
} + + {/* 左侧拖拽手柄 */} + {leftPanel && ( +
+ )} + + {/* 中间面板 */} + {middlePanel &&
{middlePanel}
} + + {/* 右侧拖拽手柄 */} + {rightPanel && ( +
+ )} + + {/* 右侧面板 */} + {rightPanel &&
{rightPanel}
}
); }; -export default ResizablePanels; +// 导出组件 +export { ResizablePanel }; +export default ResizablePanel; From 2e0b1eec464c50155673ea1fce1a8f9e5344ba47 Mon Sep 17 00:00:00 2001 From: pomelo-nwu Date: Wed, 16 Apr 2025 17:42:45 +0800 Subject: [PATCH 11/14] refactor(icons): optimize icon components and documentation - Standardize icon components implementation\n- Add theme token support for all icons\n- Remove Dump icon\n- Update documentation with better structure and examples\n- Add API documentation and icon categories\n- Keep zh-CN and en-US docs in sync --- .../studio-components/src/Icons/AddNode.tsx | 24 +++- .../studio-components/src/Icons/Arrow.tsx | 16 +-- .../studio-components/src/Icons/Database.tsx | 13 ++- packages/studio-components/src/Icons/Dump.tsx | 14 --- packages/studio-components/src/Icons/File.tsx | 27 +++-- .../src/Icons/FileExport.tsx | 16 ++- .../src/Icons/index.en-US.md | 106 ++++++++++++++++++ .../studio-components/src/Icons/index.tsx | 44 ++++++-- .../src/Icons/index.zh-CN.md | 91 ++++++++++++++- 9 files changed, 295 insertions(+), 56 deletions(-) delete mode 100644 packages/studio-components/src/Icons/Dump.tsx diff --git a/packages/studio-components/src/Icons/AddNode.tsx b/packages/studio-components/src/Icons/AddNode.tsx index 4ee6e4eb7..cab56fefa 100644 --- a/packages/studio-components/src/Icons/AddNode.tsx +++ b/packages/studio-components/src/Icons/AddNode.tsx @@ -1,17 +1,31 @@ import * as React from 'react'; +import { theme } from 'antd'; +import { IconProps } from './index'; -interface IAddNodeProps {} +/** + * 添加节点图标组件 + * 用于表示添加新节点的操作 + */ +const AddNode: React.FC = ({ style = {} }) => { + const { token } = theme.useToken(); + const { fontSize = token.fontSize, color = token.colorText } = style; -const AddNode: React.FunctionComponent = props => { return ( - + + /> ); }; diff --git a/packages/studio-components/src/Icons/Arrow.tsx b/packages/studio-components/src/Icons/Arrow.tsx index 794977325..aa5ae8d17 100644 --- a/packages/studio-components/src/Icons/Arrow.tsx +++ b/packages/studio-components/src/Icons/Arrow.tsx @@ -1,15 +1,17 @@ import * as React from 'react'; +import { theme } from 'antd'; +import { IconProps } from './index'; -interface IArrowProps { - style?: React.CSSProperties; -} - -const Arrow: React.FC = ({ style = {} }) => { - const { color = '#F97108', fontSize = '16px' } = style; +/** + * 箭头图标组件 + */ +const Arrow: React.FC = ({ style = {} }) => { + const { token } = theme.useToken(); + const { fontSize = token.fontSize, color = token.colorText } = style; return ( = props => { - const { style = {} } = props; +/** + * 数据库图标组件 + */ +const Database: React.FC = ({ style = {} }) => { const { token } = theme.useToken(); - const { fontSize = 16, color = token.colorText } = style; + const { fontSize = token.fontSize, color = token.colorText } = style; + return ( { - return ( - - ); -}; diff --git a/packages/studio-components/src/Icons/File.tsx b/packages/studio-components/src/Icons/File.tsx index 462c89548..6868d7847 100644 --- a/packages/studio-components/src/Icons/File.tsx +++ b/packages/studio-components/src/Icons/File.tsx @@ -1,13 +1,24 @@ import React from 'react'; -export interface IGraph3DProps { - style?: React.CSSProperties; - text?: string; -} -const File: React.FunctionComponent = props => { - const { style = {}, text = 'JSON' } = props; - const { fontSize = '16px', color = '#000' } = style; +import { theme } from 'antd'; +import { IconProps } from './index'; + +/** + * 文件图标组件 + */ +const File: React.FC = ({ style = {} }) => { + const { token } = theme.useToken(); + const { fontSize = token.fontSize, color = token.colorText } = style; + const text = style.text || 'JSON'; + return ( - + }) => { - const { color = '#2c2c2c', fontSize = '18px' } = style || { color: '#2c2c2c' }; +import { theme } from 'antd'; +import { IconProps } from './index'; + +/** + * 文件导出图标组件 + */ +const FileExport: React.FC = ({ style = {} }) => { + const { token } = theme.useToken(); + const { fontSize = token.fontSize, color = token.colorText } = style; + return ( }) => { + /> ); }; + +export default FileExport; diff --git a/packages/studio-components/src/Icons/index.en-US.md b/packages/studio-components/src/Icons/index.en-US.md index ee5c26e6e..3c7ba0d07 100644 --- a/packages/studio-components/src/Icons/index.en-US.md +++ b/packages/studio-components/src/Icons/index.en-US.md @@ -4,6 +4,112 @@ tag: New # Icons +A collection of icon components for GraphScope Studio, based on Ant Design design system with theme customization support. + +## Features + +- Automatic theme color adaptation +- Customizable size and color +- Text content support (for specific icons) +- Unified usage pattern + +## Examples + +### Icon Preview + +```jsx +import React from 'react'; +import { Flex, Typography } from 'antd'; +import { Icons } from '@graphscope/studio-components'; + +export default () => { + return ( + + {Object.keys(Icons).map(key => { + const Icon = Icons[key]; + return ( + + + {key} + + ); + })} + + ); +}; +``` + +### Basic Usage + +```jsx +import React from 'react'; +import { Space } from 'antd'; +import { Icons } from '@graphscope/studio-components'; + +export default () => { + return ( + + + + + + ); +}; +``` + +## API + +### IconProps + +| Property | Description | Type | Default | +| -------- | ------------------------------------------- | ----------------------------------------- | ------- | +| style | Custom styles, including fontSize and color | `React.CSSProperties & { text?: string }` | - | + +### Special Properties + +Some icons support special style properties: + +| Icon | Special Property | Description | +| ---- | ---------------- | ----------------------------------------------------------- | +| File | text | Text content displayed in the file icon, defaults to 'JSON' | + +## Icon Categories + +### File Operation Icons + +- File +- FileExport +- FileYaml + +### Graph Operation Icons + +- Graph2D +- Graph3D +- ZoomFit +- Cluster +- Lasso +- Arrow + +### Node Operation Icons + +- AddNode +- PrimaryKey +- Punctuation + +### UI Control Icons + +- Sidebar +- Explorer +- Lock +- Unlock + +### Data Related Icons + +- Database +- Qps +- Model +- Trash + ```jsx import React, { useState } from 'react'; import { Space } from 'antd'; diff --git a/packages/studio-components/src/Icons/index.tsx b/packages/studio-components/src/Icons/index.tsx index 7d0b13872..f5f769247 100644 --- a/packages/studio-components/src/Icons/index.tsx +++ b/packages/studio-components/src/Icons/index.tsx @@ -1,21 +1,47 @@ -export { default as AddNode } from './AddNode'; -export { default as PrimaryKey } from './primary-key'; -export { default as Sidebar } from './Sidebar'; -export { default as Trash } from './Trash'; -export { default as Dump } from './Dump'; +/** + * Icons 组件库 + * 提供了一系列用于界面展示的图标组件 + */ + +import { theme } from 'antd'; + +/** + * 图标组件通用属性接口 + */ +export interface IconProps { + /** 自定义样式,包含 fontSize 和 color */ + style?: React.CSSProperties & { + /** 文本内容(仅用于特定图标) */ + text?: string; + }; +} + +// 文件操作相关图标 +export { default as File } from './File'; export { default as FileExport } from './FileExport'; export { default as FileYaml } from './FileYaml'; + +// 图形操作相关图标 export { default as Graph2D } from './Graph2D'; export { default as Graph3D } from './Graph3D'; export { default as ZoomFit } from './ZoomFit'; export { default as Cluster } from './Cluster'; -export { default as Lock } from './Lock'; -export { default as Unlock } from './Unlock'; export { default as Lasso } from './Lasso'; -export { default as File } from './File'; export { default as Arrow } from './Arrow'; + +// 节点操作相关图标 +export { default as AddNode } from './AddNode'; +export { default as PrimaryKey } from './primary-key'; export { default as Punctuation } from './Punctuation'; -export { default as Database } from './Database'; + +// 界面控制相关图标 +export { default as Sidebar } from './Sidebar'; export { default as Explorer } from './Explorer'; +export { default as Lock } from './Lock'; +export { default as Unlock } from './Unlock'; + +// 数据相关图标 +export { default as Database } from './Database'; export { default as Qps } from './Qps'; export { default as Model } from './Model'; +export { default as Trash } from './Trash'; diff --git a/packages/studio-components/src/Icons/index.zh-CN.md b/packages/studio-components/src/Icons/index.zh-CN.md index b9a3c3cd8..71aa5737e 100644 --- a/packages/studio-components/src/Icons/index.zh-CN.md +++ b/packages/studio-components/src/Icons/index.zh-CN.md @@ -2,15 +2,27 @@ tag: New --- -# Icons +# Icons 图标 + +GraphScope Studio 提供的图标组件库,基于 Ant Design 的设计规范,支持主题定制。 + +## 特性 + +- 支持主题色自动适配 +- 支持自定义大小和颜色 +- 支持文本内容(特定图标) +- 统一的使用方式 + +## 代码演示 + +### 图标预览 ```jsx -import React, { useState } from 'react'; -import { Space, Flex, Typography } from 'antd'; +import React from 'react'; +import { Flex, Typography } from 'antd'; import { Icons } from '@graphscope/studio-components'; export default () => { - console.log(Icons, Object.keys(Icons)); return ( {Object.keys(Icons).map(key => { @@ -26,3 +38,74 @@ export default () => { ); }; ``` + +### 基础用法 + +```jsx +import React from 'react'; +import { Space } from 'antd'; +import { Icons } from '@graphscope/studio-components'; + +export default () => { + return ( + + + + + + ); +}; +``` + +## API + +### IconProps + +| 参数 | 说明 | 类型 | 默认值 | +| ----- | ---------------------------------- | ----------------------------------------- | ------ | +| style | 自定义样式,包含 fontSize 和 color | `React.CSSProperties & { text?: string }` | - | + +### 特殊属性 + +部分图标支持特殊的 style 属性: + +| 图标 | 特殊属性 | 说明 | +| ---- | -------- | --------------------------------------- | +| File | text | 文件图标中显示的文本内容,默认为 'JSON' | + +## 图标分类 + +### 文件操作相关图标 + +- File +- FileExport +- FileYaml + +### 图形操作相关图标 + +- Graph2D +- Graph3D +- ZoomFit +- Cluster +- Lasso +- Arrow + +### 节点操作相关图标 + +- AddNode +- PrimaryKey +- Punctuation + +### 界面控制相关图标 + +- Sidebar +- Explorer +- Lock +- Unlock + +### 数据相关图标 + +- Database +- Qps +- Model +- Trash From bb0c59a73b5cc5ffd373dcbe3987948a16a148df Mon Sep 17 00:00:00 2001 From: pomelo-nwu Date: Wed, 16 Apr 2025 22:14:08 +0800 Subject: [PATCH 12/14] docs(FullScreen): fix message import in examples and improve component documentation --- .../src/FullScreen/index.en-US.md | 132 ++++++++++++++++++ .../studio-components/src/FullScreen/index.md | 116 ++++++++++++++- .../src/FullScreen/index.tsx | 42 +++++- 3 files changed, 285 insertions(+), 5 deletions(-) create mode 100644 packages/studio-components/src/FullScreen/index.en-US.md diff --git a/packages/studio-components/src/FullScreen/index.en-US.md b/packages/studio-components/src/FullScreen/index.en-US.md new file mode 100644 index 000000000..7e40e06fa --- /dev/null +++ b/packages/studio-components/src/FullScreen/index.en-US.md @@ -0,0 +1,132 @@ +--- +title: FullScreen +group: + title: General + order: 1 +--- + +# FullScreen + +A simple component to toggle fullscreen mode for a specified container. + +## When To Use + +- When you need to toggle fullscreen mode for a specific area +- Useful in data visualization, image viewing, and similar scenarios + +## Examples + +### Basic Usage + +```jsx +import React, { useRef } from 'react'; +import { FullScreen } from '@graphscope/studio-components'; + +export default () => { + const containerRef = useRef(null); + return ( +
+ +
+ Click the button in the top-left corner to enter fullscreen mode +
+
+ ); +}; +``` + +### Custom Styling + +```jsx +import React, { useRef } from 'react'; +import { FullScreen } from '@graphscope/studio-components'; + +export default () => { + const containerRef = useRef(null); + return ( +
+ +
+ Fullscreen button with custom styling +
+
+ ); +}; +``` + +### Listen to Fullscreen State Changes + +```jsx +import React, { useRef, useState } from 'react'; +import { FullScreen } from '@graphscope/studio-components'; +import { message } from 'antd'; + +export default () => { + const containerRef = useRef(null); + const [isFullScreen, setIsFullScreen] = useState(false); + + const handleFullScreenChange = fullScreen => { + setIsFullScreen(fullScreen); + message.info(`Current fullscreen state: ${fullScreen ? 'Fullscreen' : 'Normal'}`); + }; + + return ( +
+ +
+ Fullscreen state: {isFullScreen ? 'Fullscreen' : 'Normal'} +
+
+ ); +}; +``` + +## API + +### FullScreen + +| Property | Description | Type | Default | +| ------------------ | ---------------------------------------------------------------- | -------------------------------------- | ------------ | +| containerRef | Reference to the container element to be displayed in fullscreen | React.RefObject | - | +| title | Tooltip text, supports internationalization | string \| React.ReactNode | 'Fullscreen' | +| placement | Tooltip placement | 'top' \| 'right' \| 'bottom' \| 'left' | 'left' | +| className | Custom CSS class name for the button | string | - | +| style | Custom inline styles for the button | React.CSSProperties | - | +| onFullScreenChange | Callback when fullscreen state changes | (isFullScreen: boolean) => void | - | + +``` + +``` diff --git a/packages/studio-components/src/FullScreen/index.md b/packages/studio-components/src/FullScreen/index.md index df0847eaa..bbf47f788 100644 --- a/packages/studio-components/src/FullScreen/index.md +++ b/packages/studio-components/src/FullScreen/index.md @@ -1,6 +1,27 @@ +--- +title: FullScreen 全屏 +group: + title: 通用 + order: 1 +--- + +# FullScreen 全屏 + +一个简单的全屏切换组件,可以将指定容器切换为全屏模式。 + +## 何时使用 + +- 需要将某个区域切换为全屏显示时 +- 在数据可视化、图片查看等场景下使用 + +## 代码演示 + +### 基础用法 + ```jsx -import React, { useState, useRef } from 'react'; +import React, { useRef } from 'react'; import { FullScreen } from '@graphscope/studio-components'; + export default () => { const containerRef = useRef(null); return ( @@ -8,11 +29,100 @@ export default () => {
- Trigger full screen + 点击左上角按钮进入全屏模式
); }; ``` + +### 自定义样式 + +```jsx +import React, { useRef } from 'react'; +import { FullScreen } from '@graphscope/studio-components'; + +export default () => { + const containerRef = useRef(null); + return ( +
+ +
+ 自定义样式的全屏按钮 +
+
+ ); +}; +``` + +### 监听全屏状态变化 + +```jsx +import React, { useRef, useState } from 'react'; +import { FullScreen } from '@graphscope/studio-components'; +import { message } from 'antd'; + +export default () => { + const containerRef = useRef(null); + const [isFullScreen, setIsFullScreen] = useState(false); + + const handleFullScreenChange = fullScreen => { + setIsFullScreen(fullScreen); + message.info(`当前全屏状态: ${fullScreen ? '全屏' : '非全屏'}`); + }; + + return ( +
+ +
+ 全屏状态: {isFullScreen ? '全屏' : '非全屏'} +
+
+ ); +}; +``` + +## API + +### FullScreen + +| 参数 | 说明 | 类型 | 默认值 | +| ------------------ | ---------------------- | -------------------------------------- | ------------ | +| containerRef | 需要全屏显示的容器引用 | React.RefObject | - | +| title | 提示文本,支持国际化 | string \| React.ReactNode | 'Fullscreen' | +| placement | 提示框位置 | 'top' \| 'right' \| 'bottom' \| 'left' | 'left' | +| className | 自定义按钮样式类名 | string | - | +| style | 自定义按钮样式 | React.CSSProperties | - | +| onFullScreenChange | 全屏状态变化回调 | (isFullScreen: boolean) => void | - | diff --git a/packages/studio-components/src/FullScreen/index.tsx b/packages/studio-components/src/FullScreen/index.tsx index 4303847a5..ee58ba70c 100644 --- a/packages/studio-components/src/FullScreen/index.tsx +++ b/packages/studio-components/src/FullScreen/index.tsx @@ -2,31 +2,69 @@ import * as React from 'react'; import { Button, TooltipProps, Tooltip } from 'antd'; import { FormattedMessage } from 'react-intl'; import { FullscreenOutlined, FullscreenExitOutlined } from '@ant-design/icons'; + +/** + * 全屏组件的属性接口 + */ interface IFullScreenProps { + /** 提示文本,支持国际化 */ title?: TooltipProps['title']; + /** 提示框位置 */ placement?: TooltipProps['placement']; + /** 需要全屏显示的容器引用 */ containerRef: React.RefObject; + /** 自定义按钮样式类名 */ + className?: string; + /** 自定义按钮样式 */ + style?: React.CSSProperties; + /** 全屏状态变化回调 */ + onFullScreenChange?: (isFullScreen: boolean) => void; } +/** + * 全屏组件 + * + * 提供一个按钮,点击后可以将指定容器切换为全屏模式 + */ const FullScreen: React.FunctionComponent = props => { - const { containerRef, title = 'Fullscreen', placement = 'left' } = props; + const { containerRef, title = 'Fullscreen', placement = 'left', className, style, onFullScreenChange } = props; + const [isFullScreen, setIsFullScreen] = React.useState(false); const icon = isFullScreen ? : ; + + // 处理全屏切换 const handleClick = () => { if (document.fullscreenElement) { document.exitFullscreen(); setIsFullScreen(false); + onFullScreenChange?.(false); } else { if (containerRef.current) { containerRef.current.requestFullscreen(); setIsFullScreen(true); + onFullScreenChange?.(true); } } }; + // 监听全屏状态变化 + React.useEffect(() => { + const handleFullScreenChange = () => { + const newIsFullScreen = !!document.fullscreenElement; + setIsFullScreen(newIsFullScreen); + onFullScreenChange?.(newIsFullScreen); + }; + + document.addEventListener('fullscreenchange', handleFullScreenChange); + + return () => { + document.removeEventListener('fullscreenchange', handleFullScreenChange); + }; + }, [onFullScreenChange]); + return ( } placement={placement}> -