From 4156ef55b3516b8036b538ab8272813d6971477a Mon Sep 17 00:00:00 2001 From: liuyangjuncong20202570 Date: Thu, 5 Jun 2025 16:37:30 +0800 Subject: [PATCH] feat(UI): replace antd Icons to campsits'&add Icons story --- moon/apps/web/.storybook/preview.tsx | 5 +- .../CodeView/TreeView/CloneTabs.tsx | 119 +++--- .../components/CodeView/TreeView/RepoTree.tsx | 400 +++++++++--------- .../web/components/Issues/IssueDetailPage.tsx | 11 +- moon/packages/ui/src/Icons/Icons.stories.tsx | 42 ++ 5 files changed, 307 insertions(+), 270 deletions(-) create mode 100644 moon/packages/ui/src/Icons/Icons.stories.tsx diff --git a/moon/apps/web/.storybook/preview.tsx b/moon/apps/web/.storybook/preview.tsx index 65f4f0f61..581a42ebd 100644 --- a/moon/apps/web/.storybook/preview.tsx +++ b/moon/apps/web/.storybook/preview.tsx @@ -11,6 +11,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { ThemeProvider } from '../components/Providers/ThemeProvider' import { ScopeProvider } from '../contexts/scope' +import { QueryNormalizerProvider } from '../utils/normy/QueryNormalizerProvider' const client = new QueryClient({}) @@ -20,7 +21,9 @@ const preview: Preview = { - + + + diff --git a/moon/apps/web/components/CodeView/TreeView/CloneTabs.tsx b/moon/apps/web/components/CodeView/TreeView/CloneTabs.tsx index e7a0b66c4..483c1f21b 100644 --- a/moon/apps/web/components/CodeView/TreeView/CloneTabs.tsx +++ b/moon/apps/web/components/CodeView/TreeView/CloneTabs.tsx @@ -1,69 +1,70 @@ -import React, { useEffect, useState } from 'react'; -import { Tabs, TabsProps, Button, Space, Popover, Input } from 'antd'; -import copy from 'copy-to-clipboard'; -import {CopyIcon, DownloadIcon} from '@gitmono/ui/Icons' -import { CheckOutlined, } from '@ant-design/icons'; -import { usePathname } from 'next/navigation'; +import React, { useEffect, useState } from 'react' +import { Button, Input, Popover, Space, Tabs, TabsProps } from 'antd' +import copy from 'copy-to-clipboard' +import { usePathname } from 'next/navigation' +import { CheckIcon, CopyIcon, DownloadIcon } from '@gitmono/ui/Icons' -const CloneTabs = ({ endpoint }:any) => { - const pathname = usePathname(); - const [text, setText] = useState(pathname||''); - const [copied, setCopied] = useState(false); - const [active_tab, setActiveTab] = useState('1') +const CloneTabs = ({ endpoint }: any) => { + const pathname = usePathname() + const [text, setText] = useState(pathname || '') + const [copied, setCopied] = useState(false) + const [active_tab, setActiveTab] = useState('1') - const onChange = (key: string) => { - setActiveTab(key) - }; + const onChange = (key: string) => { + setActiveTab(key) + } - useEffect(() => { - if (endpoint) { - const url = new URL(endpoint); + useEffect(() => { + if (endpoint) { + const url = new URL(endpoint) - if (active_tab === '1') { - setText(`${url.href}${pathname?.replace('/tree/', '')}.git`); - } else { - setText(`ssh://git@${url.host}${pathname?.replace('/tree', '')}.git`); - } - } - }, [pathname, active_tab, endpoint]); + if (active_tab === '1') { + setText(`${url.href}${pathname?.replace('/tree/', '')}.git`) + } else { + setText(`ssh://git@${url.host}${pathname?.replace('/tree', '')}.git`) + } + } + }, [pathname, active_tab, endpoint]) + const handleCopy = () => { + copy(text) + setCopied(true) + setTimeout(() => setCopied(false), 2000) // Reset after 2 seconds + } + const tab_items: TabsProps['items'] = [ + { + key: '1', + label: 'HTTP', + children: ( + + + - - ) - + return ( + } + trigger='click' + > + + + ) } -export default CloneTabs; +export default CloneTabs diff --git a/moon/apps/web/components/CodeView/TreeView/RepoTree.tsx b/moon/apps/web/components/CodeView/TreeView/RepoTree.tsx index 977be6edb..664c3d824 100644 --- a/moon/apps/web/components/CodeView/TreeView/RepoTree.tsx +++ b/moon/apps/web/components/CodeView/TreeView/RepoTree.tsx @@ -1,289 +1,279 @@ -import * as React from 'react'; -import { useState, useEffect, useCallback } from 'react'; -import { usePathname } from 'next/navigation'; -import { useRouter } from 'next/router'; -import FolderRounded from '@mui/icons-material/FolderRounded'; -import ArticleIcon from '@mui/icons-material/Article'; -import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; -import { TreeItemCheckbox, TreeItemContent, TreeItemGroupTransition, TreeItemIconContainer, TreeItemLabel, TreeItemRoot } from '@mui/x-tree-view/TreeItem'; -import { TreeItemDragAndDropOverlay, TreeItemIcon, TreeItemProvider, useTreeItem, useTreeItemModel, UseTreeItemParameters } from '@mui/x-tree-view'; -import { Box, IconButton } from '@mui/material'; +import * as React from 'react' +import { useCallback, useEffect, useState } from 'react' +import ArticleIcon from '@mui/icons-material/Article' +import FolderRounded from '@mui/icons-material/FolderRounded' +import { Box, IconButton } from '@mui/material' +import { + TreeItemDragAndDropOverlay, + TreeItemIcon, + TreeItemProvider, + useTreeItem, + useTreeItemModel, + UseTreeItemParameters +} from '@mui/x-tree-view' +import { RichTreeView } from '@mui/x-tree-view/RichTreeView' +import { + TreeItemCheckbox, + TreeItemContent, + TreeItemGroupTransition, + TreeItemIconContainer, + TreeItemLabel, + TreeItemRoot +} from '@mui/x-tree-view/TreeItem' +import { usePathname } from 'next/navigation' +import { useRouter } from 'next/router' interface MuiTreeNode { - id: string; - label: string; - path: string; - content_type:string; + id: string + label: string + path: string + content_type: string // isLeaf: boolean; - children?: MuiTreeNode[]; + children?: MuiTreeNode[] } - -type FileType = 'file' | 'directory'; +type FileType = 'file' | 'directory' interface ExtendedTreeItemProps { - content_type?: FileType; - id: string; - label: string; + content_type?: FileType + id: string + label: string } interface CustomLabelProps { - children: React.ReactNode; - icon?: React.ElementType; - expandable?: boolean; + children: React.ReactNode + icon?: React.ElementType + expandable?: boolean +} + +function CustomLabel({ icon: Icon, children, ...other }: CustomLabelProps) { + return ( + + {Icon && } + + {children} + + ) } - - function CustomLabel({ - icon: Icon, - children, - ...other - }: CustomLabelProps) { - return ( - - {Icon && ( - - )} - {children} - - ); +const getIconFromFileType = (fileType: FileType) => { + switch (fileType) { + case 'file': + return ArticleIcon + case 'directory': + return FolderRounded + default: + return ArticleIcon + } +} + +interface CustomTreeItemProps + extends Omit, + Omit, 'onFocus'> {} + +const CustomTreeItem = React.forwardRef(function CustomTreeItem( + props: CustomTreeItemProps, + ref: React.Ref +) { + const { id, itemId, label, disabled, children, ...other } = props + const { + getContextProviderProps, + getRootProps, + getContentProps, + getIconContainerProps, + getCheckboxProps, + getLabelProps, + getGroupTransitionProps, + getDragAndDropOverlayProps, + status + } = useTreeItem({ id, itemId, children, label, disabled, rootRef: ref }) + + const item = useTreeItemModel(itemId)! + + let icon + + if (status.expandable) { + icon = FolderRounded + } else if (item.content_type) { + icon = getIconFromFileType(item.content_type) } - - const getIconFromFileType = (fileType: FileType) => { - switch (fileType) { - case 'file': - return ArticleIcon; - case 'directory': - return FolderRounded; - default: - return ArticleIcon; - } - }; - - interface CustomTreeItemProps - extends Omit, - Omit, 'onFocus'> {} - - const CustomTreeItem = React.forwardRef(function CustomTreeItem( - props: CustomTreeItemProps, - ref: React.Ref, - ) { - const { id, itemId, label, disabled, children, ...other } = props; - const { - getContextProviderProps, - getRootProps, - getContentProps, - getIconContainerProps, - getCheckboxProps, - getLabelProps, - getGroupTransitionProps, - getDragAndDropOverlayProps, - status, - } = useTreeItem({ id, itemId, children, label, disabled, rootRef: ref }); - - const item = useTreeItemModel(itemId)!; - - let icon; - - if (status.expandable) { - icon = FolderRounded; - } else if (item.content_type) { - icon = getIconFromFileType(item.content_type); - } - const handleClick = async(event: React.MouseEvent) => { - // eslint-disable-next-line no-console - console.log(event,"event===event====") - // interactions.handleExpansion(event); - - }; - - return ( - - - - - - - - - {/* icon */} - - - - - - - - {/* label */} - - - - {children && } - - - ); + const handleClick = async (event: React.MouseEvent) => { + // eslint-disable-next-line no-console + console.log(event, 'event===event====') + // interactions.handleExpansion(event); } -); -const RepoTree = ({ directory }:any) => { - const router = useRouter(); - const pathname = usePathname(); - const [treeData, setTreeData] = useState([]); - const [expandedNodes, setExpandedNodes] = useState([]); - const [selectedNode, setSelectedNode] = useState(null); + return ( + + + + + + + + {/* icon */} + + + + + + + {/* label */} + + + + {children && } + + + ) +}) - const convertToTreeData = useCallback((directory:any) => { - return sortProjectsByType(directory)?.map(item => ({ +const RepoTree = ({ directory }: any) => { + const router = useRouter() + const pathname = usePathname() + const [treeData, setTreeData] = useState([]) + const [expandedNodes, setExpandedNodes] = useState([]) + const [selectedNode, setSelectedNode] = useState(null) + + const convertToTreeData = useCallback((directory: any) => { + return sortProjectsByType(directory)?.map((item) => ({ id: item.date + item.name, label: item.name, path: item.path, - content_type:item.content_type, + content_type: item.content_type, children: item.content_type === 'directory' ? [] : undefined - })); - }, []); + })) + }, []) useEffect(() => { - setTreeData(convertToTreeData(directory)); - }, [directory, convertToTreeData]); - + setTreeData(convertToTreeData(directory)) + }, [directory, convertToTreeData]) - const sortProjectsByType = (projects:any[]) => { + const sortProjectsByType = (projects: any[]) => { return projects?.sort((a, b) => { if (a.content_type === 'directory' && b.content_type === 'file') { - return -1; + return -1 } else if (a.content_type === 'file' && b.content_type === 'directory') { - return 1; + return 1 } else { - return 0; + return 0 } - }); - }; + }) + } const appendTreeData = (treeData: any, subItems: any, nodeId: string) => { - return treeData.map((item:any) => { + return treeData.map((item: any) => { if (item.id === nodeId) { return { ...item, children: subItems - }; + } } else if (item.children) { return { ...item, children: appendTreeData(item.children, subItems, nodeId) - }; + } } - return item; - }); - }; + return item + }) + } const handleNodeToggle = (_event: React.SyntheticEvent | null, nodeIds: string[]) => { - const newlyExpandedNodeId = nodeIds.find(id => !expandedNodes.includes(id)); - + const newlyExpandedNodeId = nodeIds.find((id) => !expandedNodes.includes(id)) // eslint-disable-next-line no-console - console.log("11111====") + console.log('11111====') if (newlyExpandedNodeId) { const loadChildren = async () => { try { - const node = findNode(treeData, newlyExpandedNodeId); + const node = findNode(treeData, newlyExpandedNodeId) // eslint-disable-next-line no-console console.log(node, 'node===node====') - if (!node) return; + if (!node) return + + let responseData + const reqPath = pathname?.replace('/tree', '') + '/' + node.label - let responseData; - const reqPath = pathname?.replace('/tree', '') + '/' + node.label; - if (node.path && node.path !== '' && node.path !== undefined) { - responseData = await fetch(`/api/tree?path=${node.path}`) - .then(response => response.json()); + responseData = await fetch(`/api/tree?path=${node.path}`).then((response) => response.json()) } else { - responseData = await fetch(`/api/tree?path=${reqPath}`) - .then(response => response.json()); + responseData = await fetch(`/api/tree?path=${reqPath}`).then((response) => response.json()) } - const subTreeData = convertToTreeData(responseData.data.data); - const newTreeData = appendTreeData(treeData, subTreeData, newlyExpandedNodeId); + const subTreeData = convertToTreeData(responseData.data.data) + const newTreeData = appendTreeData(treeData, subTreeData, newlyExpandedNodeId) - setTreeData(newTreeData); + setTreeData(newTreeData) } catch (error) { // eslint-disable-next-line no-console - console.error('Error fetching tree data:', error); + console.error('Error fetching tree data:', error) } - }; - - loadChildren(); + } + + loadChildren() } - - setExpandedNodes(nodeIds); - }; + + setExpandedNodes(nodeIds) + } const handleNodeSelect = (_event: React.SyntheticEvent | null, nodeId: string | null) => { - if (!nodeId) return; + if (!nodeId) return - setSelectedNode(nodeId); - const node = findNode(treeData, nodeId); + setSelectedNode(nodeId) + const node = findNode(treeData, nodeId) // eslint-disable-next-line no-console - console.log("node====") + console.log('node====') - if (!node) return; + if (!node) return - const real_path = pathname?.replace('/tree', ''); - const modified_path = real_path?.replace('/code/', '/code/blob/'); + const real_path = pathname?.replace('/tree', '') + const modified_path = real_path?.replace('/code/', '/code/blob/') if (node?.content_type === 'directory') { - router.push(`${pathname}/${node.label}`); + router.push(`${pathname}/${node.label}`) } else { - router.push(`/${modified_path}/${node.label}`); + router.push(`/${modified_path}/${node.label}`) } - }; + } const findNode = (data: MuiTreeNode[], nodeId: string): MuiTreeNode | null => { for (const node of data) { - if (node.id === nodeId) return node; + if (node.id === nodeId) return node if (node.children) { - const found = findNode(node.children, nodeId); + const found = findNode(node.children, nodeId) - if (found) return found; + if (found) return found } } - return null; - }; + return null + } return ( + items={treeData} + expandedItems={expandedNodes} + selectedItems={selectedNode} + onExpandedItemsChange={handleNodeToggle} + onSelectedItemsChange={handleNodeSelect} + sx={{ height: 'fit-content', flexGrow: 1, maxWidth: 400, overflowY: 'auto' }} + slots={{ item: CustomTreeItem }} + /> ) -}; +} -export default RepoTree; \ No newline at end of file +export default RepoTree diff --git a/moon/apps/web/components/Issues/IssueDetailPage.tsx b/moon/apps/web/components/Issues/IssueDetailPage.tsx index 295a5183b..c11166e77 100644 --- a/moon/apps/web/components/Issues/IssueDetailPage.tsx +++ b/moon/apps/web/components/Issues/IssueDetailPage.tsx @@ -1,11 +1,12 @@ 'use client' import React, { useCallback, useEffect, useState } from 'react' -// import { CloseCircleOutlined, CommentOutlined } from '@ant-design/icons' import { Button, Card, Flex, Space, Tabs, TabsProps, Timeline } from 'antd' import { useRouter } from 'next/router' import toast from 'react-hot-toast' +import { CanvasCommentIcon, ResolveCommentIcon } from '@gitmono/ui' + import Comment from '@/components/MrView/MRComment' import RichEditor from '@/components/MrView/rich-editor/RichEditor' import { useGetIssueDetail } from '@/hooks/issues/useGetIssueDetail' @@ -41,7 +42,7 @@ export default function IssueDetailPage({ id }: { id: string }) { conversations: [], title: '' }) - const [editorHasText, setEditorHasText] = useState(false); + const [editorHasText, setEditorHasText] = useState(false) const [buttonLoading, setButtonLoading] = useState<{ [key: string]: boolean }>({ comment: false, close: false, @@ -164,11 +165,11 @@ export default function IssueDetailPage({ id }: { id: string }) { switch (conv.conv_type) { case 'Comment': - // icon = + icon = children = break case 'Closed': - // icon = + icon = children = conv.comment } @@ -190,7 +191,7 @@ export default function IssueDetailPage({ id }: { id: string }) { {info && info.status === 'open' && ( <>

Add a comment

- +