diff --git a/packages/core/src/parse/diff-parse.ts b/packages/core/src/parse/diff-parse.ts index efe7e03..4270d58 100644 --- a/packages/core/src/parse/diff-parse.ts +++ b/packages/core/src/parse/diff-parse.ts @@ -1,6 +1,7 @@ /* eslint-disable max-lines */ // !NOTE: ALL of the diff parse logic copy from desktop, SEE https://github.com/desktop/desktop +// With mirror change // https://en.wikipedia.org/wiki/Diff_utility // diff --git a/packages/react/src/components/v2/DiffSplitExtendLineNormal_v2.tsx b/packages/react/src/components/v2/DiffSplitExtendLineNormal_v2.tsx new file mode 100644 index 0000000..e4d6141 --- /dev/null +++ b/packages/react/src/components/v2/DiffSplitExtendLineNormal_v2.tsx @@ -0,0 +1,132 @@ +import * as React from "react"; + +import { useDomWidth } from "../../hooks/useDomWidth"; +import { useSyncHeight } from "../../hooks/useSyncHeight"; +import { emptyBGName } from "../color"; +import { SplitSide } from "../DiffView"; +import { useDiffViewContext } from "../DiffViewContext"; + +import type { DiffFile } from "@git-diff-view/core"; + +const _DiffSplitExtendLine = ({ + index, + diffFile, + oldLineExtend, + newLineExtend, + side, + lineNumber, +}: { + index: number; + side: SplitSide; + oldLineExtend: { data: any }; + newLineExtend: { data: any }; + diffFile: DiffFile; + lineNumber: number; +}) => { + const { useDiffContext } = useDiffViewContext(); + + const oldLine = diffFile.getSplitLeftLine(index); + + const newLine = diffFile.getSplitRightLine(index); + + const renderExtendLine = useDiffContext(React.useCallback((s) => s.renderExtendLine, [])); + + const currentExtend = side === SplitSide.old ? oldLineExtend : newLineExtend; + + const currentLineNumber = side === SplitSide.old ? oldLine.lineNumber : newLine.lineNumber; + + const otherSide = side === SplitSide.old ? SplitSide.new : SplitSide.old; + + useSyncHeight({ + selector: `div[data-state="extend"][data-line="${lineNumber}-extend"]`, + side: currentExtend ? SplitSide[side] : SplitSide[otherSide], + enable: side === SplitSide.new && typeof renderExtendLine === "function", + }); + + const width = useDomWidth({ + selector: side === SplitSide.old ? ".old-diff-table-wrapper" : ".new-diff-table-wrapper", + enable: !!currentExtend && typeof renderExtendLine === "function", + }); + + if (!renderExtendLine) return null; + + return ( +
+ {currentExtend ? ( +
+
+ {width > 0 && + currentExtend?.data && + renderExtendLine?.({ + diffFile, + side, + lineNumber: currentLineNumber, + data: currentExtend.data, + onUpdate: diffFile.notifyAll, + })} +
+
+ ) : ( +
+ )} +
+ ); +}; + +export const DiffSplitExtendLine = ({ + index, + diffFile, + side, + lineNumber, +}: { + index: number; + side: SplitSide; + diffFile: DiffFile; + lineNumber: number; +}) => { + const { useDiffContext } = useDiffViewContext(); + + const oldLine = diffFile.getSplitLeftLine(index); + + const newLine = diffFile.getSplitRightLine(index); + + const { oldLineExtend, newLineExtend } = useDiffContext( + React.useCallback( + (s) => ({ + oldLineExtend: s.extendData?.oldFile?.[oldLine?.lineNumber], + newLineExtend: s.extendData?.newFile?.[newLine?.lineNumber], + }), + [oldLine?.lineNumber, newLine?.lineNumber] + ) + ); + + const hasExtend = oldLineExtend?.data || newLineExtend?.data; + + // if the expand action not enabled, the `isHidden` property will never change + const enableExpand = diffFile.getExpandEnabled(); + + const currentLine = side === SplitSide.old ? oldLine : newLine; + + const currentIsShow = hasExtend && (!currentLine.isHidden || !enableExpand); + + if (!currentIsShow) return null; + + return ( + <_DiffSplitExtendLine + side={side} + index={index} + diffFile={diffFile} + lineNumber={lineNumber} + oldLineExtend={oldLineExtend} + newLineExtend={newLineExtend} + /> + ); +}; diff --git a/packages/react/src/components/v2/DiffSplitExtendLineWrap_v2.tsx b/packages/react/src/components/v2/DiffSplitExtendLineWrap_v2.tsx new file mode 100644 index 0000000..762b80b --- /dev/null +++ b/packages/react/src/components/v2/DiffSplitExtendLineWrap_v2.tsx @@ -0,0 +1,125 @@ +import * as React from "react"; + +import { emptyBGName } from "../color"; +import { SplitSide } from "../DiffView"; +import { useDiffViewContext } from "../DiffViewContext"; + +import type { DiffFile } from "@git-diff-view/core"; + +const _DiffSplitExtendLine = ({ + index, + diffFile, + lineNumber, + oldLineExtend, + newLineExtend, +}: { + index: number; + diffFile: DiffFile; + lineNumber: number; + oldLineExtend: { data: any }; + newLineExtend: { data: any }; +}) => { + const { useDiffContext } = useDiffViewContext(); + + const oldLine = diffFile.getSplitLeftLine(index); + + const newLine = diffFile.getSplitRightLine(index); + + // 需要显示的时候才进行方法订阅,可以大幅度提高性能 + const renderExtendLine = useDiffContext(React.useCallback((s) => s.renderExtendLine, [])); + + if (!renderExtendLine) return null; + + return ( +
+ {oldLineExtend ? ( +
+
+ {oldLineExtend?.data && + renderExtendLine?.({ + diffFile, + side: SplitSide.old, + lineNumber: oldLine.lineNumber, + data: oldLineExtend.data, + onUpdate: diffFile.notifyAll, + })} +
+
+ ) : ( +
+ +
+ )} +
+ {newLineExtend ? ( +
+
+ {newLineExtend?.data && + renderExtendLine?.({ + diffFile, + side: SplitSide.new, + lineNumber: newLine.lineNumber, + data: newLineExtend.data, + onUpdate: diffFile.notifyAll, + })} +
+
+ ) : ( +
+ +
+ )} +
+ ); +}; + +export const DiffSplitExtendLine = ({ + index, + diffFile, + lineNumber, +}: { + index: number; + diffFile: DiffFile; + lineNumber: number; +}) => { + const { useDiffContext } = useDiffViewContext(); + + const oldLine = diffFile.getSplitLeftLine(index); + + const newLine = diffFile.getSplitRightLine(index); + + const { oldLineExtend, newLineExtend } = useDiffContext( + React.useCallback( + (s) => ({ + oldLineExtend: s.extendData?.oldFile?.[oldLine?.lineNumber], + newLineExtend: s.extendData?.newFile?.[newLine?.lineNumber], + }), + [oldLine?.lineNumber, newLine?.lineNumber] + ) + ); + + const hasExtend = oldLineExtend?.data || newLineExtend?.data; + + // if the expand action not enabled, the `isHidden` property will never change + const enableExpand = diffFile.getExpandEnabled(); + + const currentIsShow = hasExtend && ((!oldLine?.isHidden && !newLine?.isHidden) || !enableExpand); + + if (!currentIsShow) return null; + + return ( + <_DiffSplitExtendLine + index={index} + diffFile={diffFile} + lineNumber={lineNumber} + oldLineExtend={oldLineExtend} + newLineExtend={newLineExtend} + /> + ); +}; diff --git a/packages/react/src/components/v2/DiffSplitHunkLineNormal_v2.tsx b/packages/react/src/components/v2/DiffSplitHunkLineNormal_v2.tsx new file mode 100644 index 0000000..d7b66d9 --- /dev/null +++ b/packages/react/src/components/v2/DiffSplitHunkLineNormal_v2.tsx @@ -0,0 +1,297 @@ +import { composeLen, type DiffFile } from "@git-diff-view/core"; +import * as React from "react"; + +import { useSyncHeight } from "../../hooks/useSyncHeight"; +import { hunkLineNumberBGName, plainLineNumberColorName, hunkContentBGName, hunkContentColorName } from "../color"; +import { ExpandUp, ExpandDown, ExpandAll } from "../DiffExpand"; +import { SplitSide } from "../DiffView"; +import { useDiffViewContext, DiffModeEnum } from "../DiffViewContext"; +import { diffAsideWidthName } from "../tools"; + +const _DiffSplitHunkLineGitHub = ({ + index, + diffFile, + side, + lineNumber, +}: { + index: number; + side: SplitSide; + diffFile: DiffFile; + lineNumber: number; +}) => { + const currentHunk = diffFile.getSplitHunkLine(index); + + const expandEnabled = diffFile.getExpandEnabled(); + + useSyncHeight({ + selector: `div[data-state="hunk"][data-line="${lineNumber}-hunk"]`, + side: SplitSide[SplitSide.old], + enable: side === SplitSide.new, + }); + + const enableHunkAction = side === SplitSide.old; + + const couldExpand = expandEnabled && currentHunk && currentHunk.splitInfo; + + const isExpandAll = + currentHunk && + currentHunk.splitInfo && + currentHunk.splitInfo.endHiddenIndex - currentHunk.splitInfo.startHiddenIndex < composeLen; + + const isFirstLine = currentHunk && currentHunk.isFirst; + + const isLastLine = currentHunk && currentHunk.isLast; + + return ( +
+ {enableHunkAction ? ( + <> +
+ {couldExpand ? ( + isFirstLine ? ( + + ) : isLastLine ? ( + + ) : isExpandAll ? ( + + ) : ( + <> + + + + ) + ) : ( +
+ )} +
+
+ {currentHunk.splitInfo?.plainText || currentHunk.text} +
+ + ) : ( +
+ )} +
+ ); +}; + +const _DiffSplitHunkLineGitLab = ({ + index, + diffFile, + side, + lineNumber, +}: { + index: number; + side: SplitSide; + diffFile: DiffFile; + lineNumber: number; +}) => { + const currentHunk = diffFile.getSplitHunkLine(index); + + const expandEnabled = diffFile.getExpandEnabled(); + + useSyncHeight({ + selector: `div[data-state="hunk"][data-line="${lineNumber}-hunk"]`, + side: SplitSide[SplitSide.old], + enable: side === SplitSide.new, + }); + + const couldExpand = expandEnabled && currentHunk && currentHunk.splitInfo; + + const isExpandAll = + currentHunk && + currentHunk.splitInfo && + currentHunk.splitInfo.endHiddenIndex - currentHunk.splitInfo.startHiddenIndex < composeLen; + + const isFirstLine = currentHunk && currentHunk.isFirst; + + const isLastLine = currentHunk && currentHunk.isLast; + + return ( +
+
+ {couldExpand ? ( + isFirstLine ? ( + + ) : isLastLine ? ( + + ) : isExpandAll ? ( + + ) : ( + <> + + + + ) + ) : ( +
+ )} +
+
+ {currentHunk.splitInfo?.plainText || currentHunk.text} +
+
+ ); +}; + +const _DiffSplitHunkLine = ({ + index, + diffFile, + side, + lineNumber, +}: { + index: number; + side: SplitSide; + diffFile: DiffFile; + lineNumber: number; +}) => { + const { useDiffContext } = useDiffViewContext(); + + const diffViewMode = useDiffContext(React.useCallback((s) => s.mode, [])); + + if ( + diffViewMode === DiffModeEnum.SplitGitHub || + diffViewMode === DiffModeEnum.Split || + diffViewMode === DiffModeEnum.Unified + ) { + return <_DiffSplitHunkLineGitHub index={index} diffFile={diffFile} side={side} lineNumber={lineNumber} />; + } else { + return <_DiffSplitHunkLineGitLab index={index} diffFile={diffFile} side={side} lineNumber={lineNumber} />; + } +}; + +export const DiffSplitHunkLine = ({ + index, + diffFile, + side, + lineNumber, +}: { + index: number; + side: SplitSide; + diffFile: DiffFile; + lineNumber: number; +}) => { + const currentHunk = diffFile.getSplitHunkLine(index); + + const currentIsShow = + currentHunk && + currentHunk.splitInfo && + currentHunk.splitInfo.startHiddenIndex < currentHunk.splitInfo.endHiddenIndex; + + const currentIsPureHunk = currentHunk && diffFile._getIsPureDiffRender() && !currentHunk.splitInfo; + + if (!currentIsShow && !currentIsPureHunk) return null; + + return <_DiffSplitHunkLine index={index} diffFile={diffFile} side={side} lineNumber={lineNumber} />; +}; diff --git a/packages/react/src/components/v2/DiffSplitHunkLineWrap_v2.tsx b/packages/react/src/components/v2/DiffSplitHunkLineWrap_v2.tsx new file mode 100644 index 0000000..8cc99a4 --- /dev/null +++ b/packages/react/src/components/v2/DiffSplitHunkLineWrap_v2.tsx @@ -0,0 +1,325 @@ +import { composeLen, type DiffFile } from "@git-diff-view/core"; +import * as React from "react"; + +import { hunkLineNumberBGName, plainLineNumberColorName, hunkContentBGName, hunkContentColorName } from "../color"; +import { ExpandUp, ExpandDown, ExpandAll } from "../DiffExpand"; +import { useDiffViewContext, DiffModeEnum } from "../DiffViewContext"; + +const DiffSplitHunkLineGitHub = ({ + index, + diffFile, + lineNumber, +}: { + index: number; + diffFile: DiffFile; + lineNumber: number; +}) => { + const currentHunk = diffFile.getSplitHunkLine(index); + + const expandEnabled = diffFile.getExpandEnabled(); + + const couldExpand = expandEnabled && currentHunk && currentHunk.splitInfo; + + const isExpandAll = + currentHunk && + currentHunk.splitInfo && + currentHunk.splitInfo.endHiddenIndex - currentHunk.splitInfo.startHiddenIndex < composeLen; + + const currentIsShow = + currentHunk && + currentHunk.splitInfo && + currentHunk.splitInfo.startHiddenIndex < currentHunk.splitInfo.endHiddenIndex; + + const currentIsPureHunk = currentHunk && diffFile._getIsPureDiffRender() && !currentHunk.splitInfo; + + const isFirstLine = currentHunk && currentHunk.isFirst; + + const isLastLine = currentHunk && currentHunk.isLast; + + if (!currentIsShow && !currentIsPureHunk) return null; + + return ( +
+
+ {couldExpand ? ( + isFirstLine ? ( + + ) : isLastLine ? ( + + ) : isExpandAll ? ( + + ) : ( + <> + + + + ) + ) : ( +
+ )} +
+
+
+ {currentHunk.splitInfo?.plainText || currentHunk.text} +
+
+
+ ); +}; + +const DiffSplitHunkLineGitLab = ({ + index, + diffFile, + lineNumber, +}: { + index: number; + diffFile: DiffFile; + lineNumber: number; +}) => { + const currentHunk = diffFile.getSplitHunkLine(index); + + const expandEnabled = diffFile.getExpandEnabled(); + + const couldExpand = expandEnabled && currentHunk && currentHunk.splitInfo; + + const isExpandAll = + currentHunk && + currentHunk.splitInfo && + currentHunk.splitInfo.endHiddenIndex - currentHunk.splitInfo.startHiddenIndex < composeLen; + + const currentIsShow = + currentHunk && + currentHunk.splitInfo && + currentHunk.splitInfo.startHiddenIndex < currentHunk.splitInfo.endHiddenIndex; + + const currentIsPureHunk = currentHunk && diffFile._getIsPureDiffRender() && !currentHunk.splitInfo; + + const isFirstLine = currentHunk && currentHunk.isFirst; + + const isLastLine = currentHunk && currentHunk.isLast; + + if (!currentIsShow && !currentIsPureHunk) return null; + + return ( +
+
+ {couldExpand ? ( + isFirstLine ? ( + + ) : isLastLine ? ( + + ) : isExpandAll ? ( + + ) : ( + <> + + + + ) + ) : ( +
+ )} +
+
+
+ {currentHunk.splitInfo?.plainText || currentHunk.text} +
+
+
+
+ {couldExpand ? ( + isFirstLine ? ( + + ) : isLastLine ? ( + + ) : isExpandAll ? ( + + ) : ( + <> + + + + ) + ) : ( +
+ )} +
+
+
+ {currentHunk.splitInfo?.plainText || currentHunk.text} +
+
+
+ ); +}; + +export const DiffSplitHunkLine = ({ + index, + diffFile, + lineNumber, +}: { + index: number; + diffFile: DiffFile; + lineNumber: number; +}) => { + const { useDiffContext } = useDiffViewContext(); + + const diffViewMode = useDiffContext(React.useCallback((s) => s.mode, [])); + + if ( + diffViewMode === DiffModeEnum.SplitGitHub || + diffViewMode === DiffModeEnum.Split || + diffViewMode === DiffModeEnum.Unified + ) { + return ; + } else { + return ; + } +}; diff --git a/packages/react/src/components/v2/DiffSplitLineNormal_v2.tsx b/packages/react/src/components/v2/DiffSplitLineNormal_v2.tsx new file mode 100644 index 0000000..1d2c4aa --- /dev/null +++ b/packages/react/src/components/v2/DiffSplitLineNormal_v2.tsx @@ -0,0 +1,144 @@ +import { DiffLineType, type DiffFile, checkDiffLineIncludeChange } from "@git-diff-view/core"; +import * as React from "react"; + +import { getContentBG, getLineNumberBG, plainLineNumberColorName, emptyBGName } from "../color"; +import { DiffSplitAddWidget } from "../DiffAddWidget"; +import { DiffContent } from "../DiffContent"; +import { SplitSide } from "../DiffView"; +import { useDiffViewContext } from "../DiffViewContext"; +import { useDiffWidgetContext } from "../DiffWidgetContext"; +import { diffAsideWidthName } from "../tools"; + +const _DiffSplitLine = ({ + index, + diffFile, + lineNumber, + side, +}: { + index: number; + side: SplitSide; + diffFile: DiffFile; + lineNumber: number; +}) => { + const getCurrentSyntaxLine = side === SplitSide.old ? diffFile.getOldSyntaxLine : diffFile.getNewSyntaxLine; + + const oldLine = diffFile.getSplitLeftLine(index); + + const newLine = diffFile.getSplitRightLine(index); + + const currentLine = side === SplitSide.old ? oldLine : newLine; + + const hasDiff = !!currentLine?.diff; + + const hasContent = !!currentLine.lineNumber; + + const hasChange = checkDiffLineIncludeChange(currentLine?.diff); + + const isAdded = currentLine?.diff?.type === DiffLineType.Add; + + const isDelete = currentLine?.diff?.type === DiffLineType.Delete; + + const { useDiffContext } = useDiffViewContext(); + + const { enableHighlight, enableAddWidget, onAddWidgetClick } = useDiffContext( + React.useCallback( + (s) => ({ + enableHighlight: s.enableHighlight, + enableAddWidget: s.enableAddWidget, + onAddWidgetClick: s.onAddWidgetClick, + }), + [] + ) + ); + + const { useWidget } = useDiffWidgetContext(); + + const setWidget = useWidget.getReadonlyState().setWidget; + + const contentBG = getContentBG(isAdded, isDelete, hasDiff); + + const lineNumberBG = getLineNumberBG(isAdded, isDelete, hasDiff); + + const syntaxLine = getCurrentSyntaxLine(currentLine.lineNumber); + + return ( +
+ {hasContent ? ( + <> +
+ {hasDiff && enableAddWidget && ( + onAddWidgetClick.current?.(...props)} + className="absolute left-[100%] top-[50%] z-[1] translate-x-[-50%] translate-y-[-50%]" + onOpenAddWidget={(lineNumber, side) => setWidget({ lineNumber: lineNumber, side: side })} + /> + )} + + {currentLine.lineNumber} + +
+
+ +
+ + ) : ( +
+ )} +
+ ); +}; + +export const DiffSplitLine = ({ + index, + diffFile, + lineNumber, + side, +}: { + index: number; + side: SplitSide; + diffFile: DiffFile; + lineNumber: number; +}) => { + const getCurrentLine = side === SplitSide.old ? diffFile.getSplitLeftLine : diffFile.getSplitRightLine; + + const currentLine = getCurrentLine(index); + + if (currentLine?.isHidden) return null; + + return <_DiffSplitLine index={index} diffFile={diffFile} lineNumber={lineNumber} side={side} />; +}; diff --git a/packages/react/src/components/v2/DiffSplitLineWrap_v2.tsx b/packages/react/src/components/v2/DiffSplitLineWrap_v2.tsx new file mode 100644 index 0000000..68a1b70 --- /dev/null +++ b/packages/react/src/components/v2/DiffSplitLineWrap_v2.tsx @@ -0,0 +1,194 @@ +import { DiffLineType, type DiffFile, checkDiffLineIncludeChange } from "@git-diff-view/core"; +import * as React from "react"; + +import { getContentBG, getLineNumberBG, plainLineNumberColorName, emptyBGName } from "../color"; +import { DiffSplitAddWidget } from "../DiffAddWidget"; +import { DiffContent } from "../DiffContent"; +import { SplitSide } from "../DiffView"; +import { useDiffViewContext } from "../DiffViewContext"; +import { useDiffWidgetContext } from "../DiffWidgetContext"; + +const _DiffSplitLine = ({ index, diffFile, lineNumber }: { index: number; diffFile: DiffFile; lineNumber: number }) => { + const oldLine = diffFile.getSplitLeftLine(index); + + const newLine = diffFile.getSplitRightLine(index); + + const oldSyntaxLine = diffFile.getOldSyntaxLine(oldLine?.lineNumber); + + const newSyntaxLine = diffFile.getNewSyntaxLine(newLine?.lineNumber); + + const hasDiff = !!oldLine?.diff || !!newLine?.diff; + + const hasChange = checkDiffLineIncludeChange(oldLine?.diff) || checkDiffLineIncludeChange(newLine?.diff); + + const oldLineIsDelete = oldLine?.diff?.type === DiffLineType.Delete; + + const newLineIsAdded = newLine?.diff?.type === DiffLineType.Add; + + const { useDiffContext } = useDiffViewContext(); + + const { enableHighlight, enableAddWidget, onAddWidgetClick } = useDiffContext( + React.useCallback( + (s) => ({ + enableHighlight: s.enableHighlight, + enableAddWidget: s.enableAddWidget, + onAddWidgetClick: s.onAddWidgetClick, + }), + [] + ) + ); + + const { useWidget } = useDiffWidgetContext(); + + const setWidget = useWidget.getReadonlyState().setWidget; + + const hasOldLine = !!oldLine.lineNumber; + + const hasNewLine = !!newLine.lineNumber; + + const oldLineContentBG = getContentBG(false, oldLineIsDelete, hasDiff); + + const oldLineNumberBG = getLineNumberBG(false, oldLineIsDelete, hasDiff); + + const newLineContentBG = getContentBG(newLineIsAdded, false, hasDiff); + + const newLineNumberBG = getLineNumberBG(newLineIsAdded, false, hasDiff); + + return ( +
+ {hasOldLine ? ( + <> +
+ {hasDiff && enableAddWidget && ( + onAddWidgetClick.current?.(...props)} + className="absolute left-[100%] z-[1] translate-x-[-50%]" + onOpenAddWidget={(lineNumber, side) => setWidget({ lineNumber: lineNumber, side: side })} + /> + )} + + {oldLine.lineNumber} + +
+
+ {hasDiff && enableAddWidget && ( + onAddWidgetClick.current?.(...props)} + className="absolute right-[100%] z-[1] translate-x-[50%]" + onOpenAddWidget={(lineNumber, side) => setWidget({ lineNumber: lineNumber, side: side })} + /> + )} + +
+ + ) : ( +
+ +
+ )} +
+ {hasNewLine ? ( + <> +
+ {hasDiff && enableAddWidget && ( + onAddWidgetClick.current?.(...props)} + className="absolute left-[100%] z-[1] translate-x-[-50%]" + onOpenAddWidget={(lineNumber, side) => setWidget({ lineNumber: lineNumber, side: side })} + /> + )} + + {newLine.lineNumber} + +
+
+ {hasDiff && enableAddWidget && ( + onAddWidgetClick.current?.(...props)} + className="absolute right-[100%] z-[1] translate-x-[50%]" + onOpenAddWidget={(lineNumber, side) => setWidget({ lineNumber: lineNumber, side: side })} + /> + )} + +
+ + ) : ( +
+ +
+ )} +
+ ); +}; + +export const DiffSplitLine = ({ + index, + diffFile, + lineNumber, +}: { + index: number; + diffFile: DiffFile; + lineNumber: number; +}) => { + const oldLine = diffFile.getSplitLeftLine(index); + + const newLine = diffFile.getSplitRightLine(index); + + if (oldLine?.isHidden && newLine?.isHidden) return null; + + return <_DiffSplitLine index={index} diffFile={diffFile} lineNumber={lineNumber} />; +}; diff --git a/packages/react/src/components/v2/DiffSplitViewLineNormal_v2.tsx b/packages/react/src/components/v2/DiffSplitViewLineNormal_v2.tsx new file mode 100644 index 0000000..2d8321f --- /dev/null +++ b/packages/react/src/components/v2/DiffSplitViewLineNormal_v2.tsx @@ -0,0 +1,35 @@ +import { DiffFileLineType } from "@git-diff-view/core"; +import * as React from "react"; + + +import { DiffSplitExtendLine } from "./DiffSplitExtendLineNormal_v2"; +import { DiffSplitHunkLine } from "./DiffSplitHunkLineNormal_v2"; +import { DiffSplitLine } from "./DiffSplitLineNormal_v2"; +import { DiffSplitWidgetLine } from "./DiffSplitWidgetLineNormal_v2"; + +import type { SplitSide } from "../DiffView"; +import type { DiffFile, DiffSplitLineItem } from "@git-diff-view/core"; + +// TODO +export const DiffSplitViewLine = ({ + line, + side, + diffFile, +}: { + line: DiffSplitLineItem; + side: SplitSide; + diffFile: DiffFile; +}) => { + switch (line.type) { + case DiffFileLineType.hunk: + return ; + case DiffFileLineType.content: + return ; + case DiffFileLineType.widget: + return ; + case DiffFileLineType.extend: + return ; + default: + return ; + } +}; diff --git a/packages/react/src/components/v2/DiffSplitViewLineWrap_v2.tsx b/packages/react/src/components/v2/DiffSplitViewLineWrap_v2.tsx new file mode 100644 index 0000000..aa0fc51 --- /dev/null +++ b/packages/react/src/components/v2/DiffSplitViewLineWrap_v2.tsx @@ -0,0 +1,25 @@ +import { DiffFileLineType } from "@git-diff-view/core"; +import * as React from "react"; + +import { DiffSplitExtendLine } from "./DiffSplitExtendLineWrap_v2"; +import { DiffSplitHunkLine } from "./DiffSplitHunkLineWrap_v2"; +import { DiffSplitLine } from "./DiffSplitLineWrap_v2"; +import { DiffSplitWidgetLine } from "./DiffSplitWidgetLineWrap_v2"; + +import type { DiffFile, DiffSplitLineItem } from "@git-diff-view/core"; + +// TODO +export const DiffSplitViewLine = ({ line, diffFile }: { line: DiffSplitLineItem; diffFile: DiffFile }) => { + switch (line.type) { + case DiffFileLineType.hunk: + return ; + case DiffFileLineType.content: + return ; + case DiffFileLineType.widget: + return ; + case DiffFileLineType.extend: + return ; + default: + return ; + } +}; diff --git a/packages/react/src/components/v2/DiffSplitViewNormal_v2.tsx b/packages/react/src/components/v2/DiffSplitViewNormal_v2.tsx new file mode 100644 index 0000000..fe016db --- /dev/null +++ b/packages/react/src/components/v2/DiffSplitViewNormal_v2.tsx @@ -0,0 +1,113 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import { DiffFileLineType, getSplitLines, type DiffFile } from "@git-diff-view/core"; +import { memo, useCallback, useEffect, useRef } from "react"; +import * as React from "react"; +import { useSyncExternalStore } from "use-sync-external-store/shim/index.js"; + + +import { useTextWidth } from "../../hooks/useTextWidth"; +import { SplitSide } from "../DiffView"; +import { useDiffViewContext } from "../DiffViewContext"; +import { removeAllSelection, syncScroll, diffFontSizeName } from "../tools"; + +import { DiffSplitViewLine } from "./DiffSplitViewLineNormal_v2"; + +import type { MouseEventHandler } from "react"; + +const onMouseDown: MouseEventHandler = (e) => { + const ele = e.target; + + // need remove all the selection + if (ele && ele instanceof HTMLElement && ele.nodeName === "BUTTON") { + removeAllSelection(); + return; + } +}; + +export const DiffSplitViewTable = ({ side, diffFile }: { side: SplitSide; diffFile: DiffFile }) => { + const className = side === SplitSide.new ? "new-diff-table" : "old-diff-table"; + + const lines = getSplitLines(diffFile); + + return ( +
+
+ {lines.map((line) => ( + + ))} +
+
+ ); +}; + +export const DiffSplitViewNormal = memo(({ diffFile }: { diffFile: DiffFile }) => { + const ref1 = useRef(null); + + const ref2 = useRef(null); + + const splitLineLength = Math.max(diffFile.splitLineLength, diffFile.fileLineLength); + + const { useDiffContext } = useDiffViewContext(); + + const fontSize = useDiffContext(useCallback((s) => s.fontSize, [])); + + useSyncExternalStore(diffFile.subscribe, diffFile.getUpdateCount); + + useEffect(() => { + const left = ref1.current; + const right = ref2.current; + if (!left || !right) return; + return syncScroll(left, right); + }, []); + + const font = React.useMemo( + () => ({ fontSize: fontSize + "px", fontFamily: "Menlo, Consolas, monospace" }), + [fontSize] + ); + + const _width = useTextWidth({ + text: splitLineLength.toString(), + font, + }); + + const width = Math.max(40, _width + 25); + + return ( +
+
+ +
+
+
+ +
+
+ ); +}); + +DiffSplitViewNormal.displayName = "DiffSplitViewNormal"; diff --git a/packages/react/src/components/v2/DiffSplitViewWrap_v2.tsx b/packages/react/src/components/v2/DiffSplitViewWrap_v2.tsx new file mode 100644 index 0000000..8c673f7 --- /dev/null +++ b/packages/react/src/components/v2/DiffSplitViewWrap_v2.tsx @@ -0,0 +1,109 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import { type DiffFile, getSplitLines } from "@git-diff-view/core"; +import { memo, useCallback, useMemo } from "react"; +import * as React from "react"; +// SEE https://github.com/facebook/react/pull/25231 +import { useSyncExternalStore } from "use-sync-external-store/shim/index.js"; + +import { useTextWidth } from "../../hooks/useTextWidth"; +import { SplitSide } from "../DiffView"; +import { useDiffViewContext } from "../DiffViewContext"; +import { createDiffSplitConfigStore, removeAllSelection, diffFontSizeName } from "../tools"; + +import { DiffSplitViewLine } from "./DiffSplitViewLineWrap_v2"; + +import type { MouseEventHandler } from "react"; +import type { Ref, UseSelectorWithStore } from "reactivity-store"; + +const Style = ({ + useSelector, + id, +}: { + useSelector: UseSelectorWithStore<{ splitRef: Ref }>; + id: string; +}) => { + const splitRef = useSelector((s) => s.splitRef); + + return ( + + ); +}; + +export const DiffSplitViewWrap = memo(({ diffFile }: { diffFile: DiffFile }) => { + const splitLineLength = Math.max(diffFile.splitLineLength, diffFile.fileLineLength); + + const { useDiffContext } = useDiffViewContext(); + + const useSplitConfig = useMemo(() => createDiffSplitConfigStore(), []); + + const fontSize = useDiffContext(useCallback((s) => s.fontSize, [])); + + useSyncExternalStore(diffFile.subscribe, diffFile.getUpdateCount); + + const onMouseDown = useCallback>((e) => { + let ele = e.target; + + const setSelectSide = useSplitConfig.getReadonlyState().setSplit; + + // need remove all the selection + if (ele && ele instanceof HTMLElement && ele.nodeName === "BUTTON") { + removeAllSelection(); + return; + } + + while (ele && ele instanceof HTMLElement && ele.nodeName !== "TD") { + ele = ele.parentElement; + } + + if (ele instanceof HTMLElement) { + const side = ele.getAttribute("data-side"); + if (side) { + setSelectSide(SplitSide[side]); + removeAllSelection(); + } + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const font = useMemo(() => ({ fontSize: fontSize + "px", fontFamily: "Menlo, Consolas, monospace" }), [fontSize]); + + const _width = useTextWidth({ + text: splitLineLength.toString(), + font, + }); + + const width = Math.max(40, _width + 25); + + const lines = getSplitLines(diffFile); + + return ( +
+
+