diff --git a/moon/apps/web/components/DiffView/FileDiff.tsx b/moon/apps/web/components/DiffView/FileDiff.tsx index 8550cbd64..e4456a31f 100644 --- a/moon/apps/web/components/DiffView/FileDiff.tsx +++ b/moon/apps/web/components/DiffView/FileDiff.tsx @@ -1,17 +1,22 @@ import { useEffect, useMemo, useRef, useState } from 'react' import { DiffFile, DiffModeEnum, DiffView } from '@git-diff-view/react' +import { CommonResultFilesChangedList } from '@gitmono/types/generated' import { ExpandIcon, SparklesIcon } from '@gitmono/ui/Icons' import { cn } from '@gitmono/ui/src/utils' + import { parsedDiffs } from '@/components/DiffView/parsedDiffs' + +import StableTreeView from './StableTreeView' + // import TreeView from './TreeView' function calculateDiffStatsFromRawDiff(diffText: string): { additions: number; deletions: number } { - const lines = diffText.split('\n'); + const lines = diffText.split('\n') - let additions = 0; + let additions = 0 - let deletions = 0; + let deletions = 0 for (const line of lines) { if (line.startsWith('+') && !line.startsWith('+++')) { @@ -24,66 +29,75 @@ function calculateDiffStatsFromRawDiff(diffText: string): { additions: number; d return { additions, deletions } } -function generateParsedFiles(diffFiles: { path: string; lang: string; diff: string }[]): { - file: { path: string; lang: string; diff: string }; - instance: DiffFile | null; - stats: { additions: number; deletions: number } +function generateParsedFiles(diffFiles: { path: string; lang: string; diff: string }[]): { + file: { path: string; lang: string; diff: string } + instance: DiffFile | null + stats: { additions: number; deletions: number } }[] { return diffFiles.map((file) => { if (file.lang === 'binary') { return { file, instance: null, - stats: { additions: 0, deletions: 0 }, - }; + stats: { additions: 0, deletions: 0 } + } } - const instance = new DiffFile('', '', '', '', [file.diff], file.lang); + const instance = new DiffFile('', '', '', '', [file.diff], file.lang) try { - instance.init(); - instance.buildSplitDiffLines(); - instance.buildUnifiedDiffLines(); + instance.init() + instance.buildSplitDiffLines() + instance.buildUnifiedDiffLines() } catch (e) { /* eslint-disable-next-line no-console */ - console.error("error:", e); + console.error('error:', e) } - const stats = calculateDiffStatsFromRawDiff(file.diff); + const stats = calculateDiffStatsFromRawDiff(file.diff) return { file, instance, stats } }) } -export default function FileDiff({ diffs }: { diffs: string }) { - const diffFiles = useMemo(() => parsedDiffs(diffs), [diffs]); +export default function FileDiff({ + diffs, + treeData +}: { + diffs: string + treeData: CommonResultFilesChangedList['data'] +}) { + const diffFiles = useMemo(() => parsedDiffs(diffs), [diffs]) - const parsedFiles = useMemo(() => generateParsedFiles(diffFiles), [diffFiles]); + const parsedFiles = useMemo(() => generateParsedFiles(diffFiles), [diffFiles]) - const [selectedPath, setSelectedPath] = useState(null); + const [_selectedPath, setSelectedPath] = useState(null) const [expandedMap, setExpandedMap] = useState>(() => Object.fromEntries(diffFiles.map((f) => [f.path, false])) - ); - - const fileRefs = useRef>({}); + ) + + const fileRefs = useRef>({}) const toggleExpanded = (path: string) => { setExpandedMap((prev) => ({ ...prev, [path]: !prev[path] })) - }; + } useEffect(() => { - setExpandedMap(Object.fromEntries(diffFiles.map((f) => [f.path, false]))); - }, [diffFiles]); - - const RenderDiffView = ({ file, instance }: { - file: { path: string; lang: string; diff: string }; - instance: DiffFile | null; + setExpandedMap(Object.fromEntries(diffFiles.map((f) => [f.path, false]))) + }, [diffFiles]) + + const RenderDiffView = ({ + file, + instance + }: { + file: { path: string; lang: string; diff: string } + instance: DiffFile | null }) => { if (file.lang === 'binary' || instance === null) { - return
Binary file
- }else if(file.diff === 'EMPTY_DIFF_MARKER\n') { - return
No change
+ return
Binary file
+ } else if (file.diff === 'EMPTY_DIFF_MARKER\n') { + return
No change
} return ( @@ -98,32 +112,22 @@ export default function FileDiff({ diffs }: { diffs: string }) { } return ( -
-
-
    - {parsedFiles.map(({ file }) => ( -
  • { - setSelectedPath(file.path) - setExpandedMap((prev) => ({ ...prev, [file.path]: true })) - const el = fileRefs.current[file.path] - - if (el) { - el.scrollIntoView({ behavior: 'smooth', block: 'start' }) - } - }} - className={cn('px-2 py-1 text-sm cursor-pointer rounded-md mb-1', selectedPath === file.path ? 'bg-[#e6f0ff]' : 'bg-transparent')} - > - {file.path} -
  • - )) - } - -
- {/* */} +
+
+ {treeData?.mui_trees && ( + { + setSelectedPath(file) + setExpandedMap((prev) => ({ ...prev, [file]: true })) + const el = fileRefs.current[file] + + if (el) { + el.scrollIntoView({ behavior: 'smooth', block: 'start' }) + } + }} + /> + )}
@@ -138,9 +142,12 @@ export default function FileDiff({ diffs }: { diffs: string }) { >
toggleExpanded(file.path)} - className={cn('flex items-center justify-between px-4 py-2 text-sm', isExpanded && 'border-b border-gray-300')} + className={cn( + 'flex items-center justify-between px-4 py-2 text-sm', + isExpanded && 'border-b border-gray-300' + )} > - + {isExpanded ? ( ) : ( @@ -153,10 +160,8 @@ export default function FileDiff({ diffs }: { diffs: string }) { −{stats.deletions}
- -
- {isExpanded && } -
+ +
{isExpanded && }
) })} diff --git a/moon/apps/web/components/DiffView/StableTreeView.tsx b/moon/apps/web/components/DiffView/StableTreeView.tsx new file mode 100644 index 000000000..62fbb0c96 --- /dev/null +++ b/moon/apps/web/components/DiffView/StableTreeView.tsx @@ -0,0 +1,70 @@ +import { memo, useRef } from 'react' +import { FileIcon } from '@primer/octicons-react' +import { TreeView } from '@primer/react' + +import { MuiTreeNode } from '@gitmono/types/generated' + +const TreeViewItem = TreeView.Item +const TreeViewSub = TreeView.SubTree + +const StableTreeView = ({ + treeData, + handleClick +}: { + treeData: MuiTreeNode[] + handleClick?: (file: string) => void +}) => { + const currentId = useRef('') + + return ( + + handleClick?.(file)} currentId={currentId} /> + + ) +} + +const TreeLoop = ({ + nodes, + basePath, + onSelect, + currentId +}: { + nodes: MuiTreeNode[] + basePath?: string + onSelect?: (path: string) => void + currentId: React.MutableRefObject +}) => { + return ( + <> + {nodes.map((node) => { + const currentPath = basePath ? `${basePath}/${node.label}` : node.label + + return ( + { + currentId.current = node.id + onSelect?.(currentPath) + }} + current={currentId.current === node.id && !node.children} + > + + {node.children && node.children.length > 0 ? : } + + {node.label} + + {node.children && node.children.length > 0 && ( + + + + )} + + ) + })} + + ) +} + +export default memo(StableTreeView) diff --git a/moon/apps/web/pages/[org]/mr/[link]/index.tsx b/moon/apps/web/pages/[org]/mr/[link]/index.tsx index 633be9de0..9eef2d614 100644 --- a/moon/apps/web/pages/[org]/mr/[link]/index.tsx +++ b/moon/apps/web/pages/[org]/mr/[link]/index.tsx @@ -214,7 +214,7 @@ const MRDetailPage: PageWithLayout = () => {
{detailIsLoading ? ( -
+
) : ( @@ -372,7 +372,7 @@ const MRDetailPage: PageWithLayout = () => {
) : MrFilesChangedData?.data?.content ? ( - + ) : (
No files changed
)}