@git-diff-view/react 0.0.26 → 0.0.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.development.js +431 -290
- package/dist/cjs/index.development.js.map +1 -1
- package/dist/cjs/index.production.js +431 -290
- package/dist/cjs/index.production.js.map +1 -1
- package/dist/css/diff-view-pure.css +4 -0
- package/dist/css/diff-view.css +4 -0
- package/dist/esm/index.mjs +362 -224
- package/dist/esm/index.mjs.map +1 -1
- package/index.d.ts +303 -23
- package/package.json +4 -3
- package/src/_base.css +3 -0
- package/src/_base_pure.css +2 -0
- package/src/_com.css +172 -0
- package/src/_theme.css +2 -0
- package/src/components/DiffAddWidget.tsx +86 -0
- package/src/components/DiffContent.tsx +367 -0
- package/src/components/DiffContent_v2.tsx +344 -0
- package/src/components/DiffExpand.tsx +25 -0
- package/src/components/DiffNoNewLine.tsx +10 -0
- package/src/components/DiffSplitContentLineNormal.tsx +164 -0
- package/src/components/DiffSplitContentLineWrap.tsx +234 -0
- package/src/components/DiffSplitExtendLineNormal.tsx +150 -0
- package/src/components/DiffSplitExtendLineWrap.tsx +133 -0
- package/src/components/DiffSplitHunkLineNormal.tsx +316 -0
- package/src/components/DiffSplitHunkLineWrap.tsx +340 -0
- package/src/components/DiffSplitView.tsx +46 -0
- package/src/components/DiffSplitViewNormal.tsx +205 -0
- package/src/components/DiffSplitViewWrap.tsx +141 -0
- package/src/components/DiffSplitWidgetLineNormal.tsx +149 -0
- package/src/components/DiffSplitWidgetLineWrap.tsx +127 -0
- package/src/components/DiffUnifiedContentLine.tsx +342 -0
- package/src/components/DiffUnifiedExtendLine.tsx +103 -0
- package/src/components/DiffUnifiedHunkLine.tsx +148 -0
- package/src/components/DiffUnifiedView.tsx +159 -0
- package/src/components/DiffUnifiedWidgetLine.tsx +104 -0
- package/src/components/DiffView.tsx +365 -0
- package/src/components/DiffViewContext.ts +11 -0
- package/src/components/DiffWidgetContext.ts +17 -0
- package/src/components/tools.ts +132 -0
- package/src/components/v2/DiffSplitContentLineNormal_v2.tsx +152 -0
- package/src/components/v2/DiffSplitContentLineWrap_v2.tsx +259 -0
- package/src/components/v2/DiffSplitExtendLineNormal_v2.tsx +146 -0
- package/src/components/v2/DiffSplitExtendLineWrap_v2.tsx +123 -0
- package/src/components/v2/DiffSplitHunkLineNormal_v2.tsx +302 -0
- package/src/components/v2/DiffSplitHunkLineWrap_v2.tsx +326 -0
- package/src/components/v2/DiffSplitViewLineNormal_v2.tsx +33 -0
- package/src/components/v2/DiffSplitViewLineWrap_v2.tsx +24 -0
- package/src/components/v2/DiffSplitViewNormal_v2.tsx +159 -0
- package/src/components/v2/DiffSplitViewWrap_v2.tsx +104 -0
- package/src/components/v2/DiffSplitView_v2.tsx +47 -0
- package/src/components/v2/DiffSplitWidgetLineNormal_v2.tsx +132 -0
- package/src/components/v2/DiffSplitWidgetLineWrap_v2.tsx +119 -0
- package/src/global.d.ts +12 -0
- package/src/hooks/useCallbackRef.ts +18 -0
- package/src/hooks/useDomWidth.ts +67 -0
- package/src/hooks/useIsMounted.ts +11 -0
- package/src/hooks/useSafeLayout.ts +5 -0
- package/src/hooks/useSyncHeight.ts +87 -0
- package/src/hooks/useTextWidth.ts +27 -0
- package/src/hooks/useUnmount.ts +10 -0
- package/src/index.ts +3 -0
- package/src/tailwind.css +3 -0
- package/src/tailwind_pure.css +3 -0
- package/styles/diff-view-pure.css +4 -0
- package/styles/diff-view.css +4 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { createStore, ref } from "reactivity-store";
|
|
2
|
+
|
|
3
|
+
import type { DiffModeEnum, DiffViewProps, SplitSide } from "./DiffView";
|
|
4
|
+
import type { RefObject } from "react";
|
|
5
|
+
|
|
6
|
+
export const createDiffConfigStore = (props: DiffViewProps<any> & { isMounted: boolean }, diffFileId: string) => {
|
|
7
|
+
return createStore(() => {
|
|
8
|
+
const id = ref(diffFileId);
|
|
9
|
+
|
|
10
|
+
const setId = (_id: string) => (id.value = _id);
|
|
11
|
+
|
|
12
|
+
const mode = ref(props.diffViewMode);
|
|
13
|
+
|
|
14
|
+
const setMode = (_mode: DiffModeEnum) => (mode.value = _mode);
|
|
15
|
+
|
|
16
|
+
const mounted = ref(props.isMounted);
|
|
17
|
+
|
|
18
|
+
const setMounted = (_mounted: boolean) => (mounted.value = _mounted);
|
|
19
|
+
|
|
20
|
+
const enableWrap = ref(props.diffViewWrap);
|
|
21
|
+
|
|
22
|
+
const setEnableWrap = (_enableWrap: boolean) => (enableWrap.value = _enableWrap);
|
|
23
|
+
|
|
24
|
+
const enableAddWidget = ref(props.diffViewAddWidget);
|
|
25
|
+
|
|
26
|
+
const setEnableAddWidget = (_enableAddWidget: boolean) => (enableAddWidget.value = _enableAddWidget);
|
|
27
|
+
|
|
28
|
+
const enableHighlight = ref(props.diffViewHighlight);
|
|
29
|
+
|
|
30
|
+
const setEnableHighlight = (_enableHighlight: boolean) => (enableHighlight.value = _enableHighlight);
|
|
31
|
+
|
|
32
|
+
const fontSize = ref(props.diffViewFontSize);
|
|
33
|
+
|
|
34
|
+
const setFontSize = (_fontSize: number) => (fontSize.value = _fontSize);
|
|
35
|
+
|
|
36
|
+
const extendData = ref({
|
|
37
|
+
oldFile: { ...props.extendData?.oldFile },
|
|
38
|
+
newFile: { ...props.extendData?.newFile },
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const setExtendData = (_extendData: DiffViewProps<any>["extendData"]) => {
|
|
42
|
+
const existOldKeys = Object.keys(extendData.value.oldFile || {});
|
|
43
|
+
const inComingOldKeys = Object.keys(_extendData.oldFile || {});
|
|
44
|
+
for (const key of existOldKeys) {
|
|
45
|
+
if (!inComingOldKeys.includes(key)) {
|
|
46
|
+
delete extendData.value.oldFile[key];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
for (const key of inComingOldKeys) {
|
|
50
|
+
extendData.value.oldFile[key] = _extendData.oldFile[key];
|
|
51
|
+
}
|
|
52
|
+
const existNewKeys = Object.keys(extendData.value.newFile || {});
|
|
53
|
+
const inComingNewKeys = Object.keys(_extendData.newFile || {});
|
|
54
|
+
for (const key of existNewKeys) {
|
|
55
|
+
if (!inComingNewKeys.includes(key)) {
|
|
56
|
+
delete extendData.value.newFile[key];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
for (const key of inComingNewKeys) {
|
|
60
|
+
extendData.value.newFile[key] = _extendData.newFile[key];
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const renderWidgetLine = ref(props.renderWidgetLine);
|
|
65
|
+
|
|
66
|
+
const setRenderWidgetLine = (_renderWidgetLine: typeof renderWidgetLine.value) =>
|
|
67
|
+
(renderWidgetLine.value = _renderWidgetLine);
|
|
68
|
+
|
|
69
|
+
const renderExtendLine = ref(props.renderExtendLine);
|
|
70
|
+
|
|
71
|
+
const setRenderExtendLine = (_renderExtendLine: typeof renderExtendLine.value) =>
|
|
72
|
+
(renderExtendLine.value = _renderExtendLine);
|
|
73
|
+
|
|
74
|
+
const onCreateUseWidgetHook = ref(props.onCreateUseWidgetHook);
|
|
75
|
+
|
|
76
|
+
const setOnCreateUseWidgetHook = (_onCreateUseWidgetHook: typeof onCreateUseWidgetHook.value) =>
|
|
77
|
+
(onCreateUseWidgetHook.value = _onCreateUseWidgetHook);
|
|
78
|
+
|
|
79
|
+
// 避免无意义的订阅
|
|
80
|
+
const onAddWidgetClick = { current: props.onAddWidgetClick };
|
|
81
|
+
|
|
82
|
+
const setOnAddWidgetClick = (_onAddWidgetClick: typeof onAddWidgetClick) =>
|
|
83
|
+
(onAddWidgetClick.current = _onAddWidgetClick.current);
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
id,
|
|
87
|
+
setId,
|
|
88
|
+
mode,
|
|
89
|
+
setMode,
|
|
90
|
+
mounted,
|
|
91
|
+
setMounted,
|
|
92
|
+
enableWrap,
|
|
93
|
+
setEnableWrap,
|
|
94
|
+
enableAddWidget,
|
|
95
|
+
setEnableAddWidget,
|
|
96
|
+
enableHighlight,
|
|
97
|
+
setEnableHighlight,
|
|
98
|
+
fontSize,
|
|
99
|
+
setFontSize,
|
|
100
|
+
extendData,
|
|
101
|
+
setExtendData,
|
|
102
|
+
renderWidgetLine,
|
|
103
|
+
setRenderWidgetLine,
|
|
104
|
+
renderExtendLine,
|
|
105
|
+
setRenderExtendLine,
|
|
106
|
+
onAddWidgetClick,
|
|
107
|
+
setOnAddWidgetClick,
|
|
108
|
+
onCreateUseWidgetHook,
|
|
109
|
+
setOnCreateUseWidgetHook,
|
|
110
|
+
};
|
|
111
|
+
});
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export const createDiffWidgetStore = (useDiffContextRef: RefObject<ReturnType<typeof createDiffConfigStore>>) => {
|
|
115
|
+
return createStore(() => {
|
|
116
|
+
const widgetSide = ref<SplitSide>(undefined);
|
|
117
|
+
|
|
118
|
+
const widgetLineNumber = ref<number>(undefined);
|
|
119
|
+
|
|
120
|
+
const setWidget = ({ side, lineNumber }: { side?: SplitSide; lineNumber?: number }) => {
|
|
121
|
+
const { renderWidgetLine } = useDiffContextRef.current?.getReadonlyState?.() || {};
|
|
122
|
+
|
|
123
|
+
if (typeof renderWidgetLine !== "function") return;
|
|
124
|
+
|
|
125
|
+
widgetSide.value = side;
|
|
126
|
+
|
|
127
|
+
widgetLineNumber.value = lineNumber;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
return { widgetSide, widgetLineNumber, setWidget };
|
|
131
|
+
});
|
|
132
|
+
};
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { DiffLineType, type DiffFile, checkDiffLineIncludeChange } from "@git-diff-view/core";
|
|
2
|
+
import {
|
|
3
|
+
getContentBG,
|
|
4
|
+
getLineNumberBG,
|
|
5
|
+
plainLineNumberColorName,
|
|
6
|
+
emptyBGName,
|
|
7
|
+
diffAsideWidthName,
|
|
8
|
+
expandLineNumberColorName,
|
|
9
|
+
} from "@git-diff-view/utils";
|
|
10
|
+
import * as React from "react";
|
|
11
|
+
|
|
12
|
+
import { DiffSplitAddWidget } from "../DiffAddWidget";
|
|
13
|
+
import { DiffContent } from "../DiffContent";
|
|
14
|
+
import { SplitSide } from "../DiffView";
|
|
15
|
+
import { useDiffViewContext } from "../DiffViewContext";
|
|
16
|
+
import { useDiffWidgetContext } from "../DiffWidgetContext";
|
|
17
|
+
|
|
18
|
+
const InternalDiffSplitLine = ({
|
|
19
|
+
index,
|
|
20
|
+
diffFile,
|
|
21
|
+
lineNumber,
|
|
22
|
+
side,
|
|
23
|
+
}: {
|
|
24
|
+
index: number;
|
|
25
|
+
side: SplitSide;
|
|
26
|
+
diffFile: DiffFile;
|
|
27
|
+
lineNumber: number;
|
|
28
|
+
}) => {
|
|
29
|
+
const getCurrentSyntaxLine = side === SplitSide.old ? diffFile.getOldSyntaxLine : diffFile.getNewSyntaxLine;
|
|
30
|
+
|
|
31
|
+
const getCurrentPlainLine = side === SplitSide.old ? diffFile.getOldPlainLine : diffFile.getNewPlainLine;
|
|
32
|
+
|
|
33
|
+
const oldLine = diffFile.getSplitLeftLine(index);
|
|
34
|
+
|
|
35
|
+
const newLine = diffFile.getSplitRightLine(index);
|
|
36
|
+
|
|
37
|
+
const currentLine = side === SplitSide.old ? oldLine : newLine;
|
|
38
|
+
|
|
39
|
+
const hasDiff = !!currentLine?.diff;
|
|
40
|
+
|
|
41
|
+
const hasContent = !!currentLine.lineNumber;
|
|
42
|
+
|
|
43
|
+
const hasChange = checkDiffLineIncludeChange(currentLine?.diff);
|
|
44
|
+
|
|
45
|
+
const isAdded = currentLine?.diff?.type === DiffLineType.Add;
|
|
46
|
+
|
|
47
|
+
const isDelete = currentLine?.diff?.type === DiffLineType.Delete;
|
|
48
|
+
|
|
49
|
+
const { useDiffContext } = useDiffViewContext();
|
|
50
|
+
|
|
51
|
+
const { enableHighlight, enableAddWidget, onAddWidgetClick } = useDiffContext.useShallowStableSelector((s) => ({
|
|
52
|
+
enableHighlight: s.enableHighlight,
|
|
53
|
+
enableAddWidget: s.enableAddWidget,
|
|
54
|
+
onAddWidgetClick: s.onAddWidgetClick,
|
|
55
|
+
}));
|
|
56
|
+
|
|
57
|
+
const { useWidget } = useDiffWidgetContext();
|
|
58
|
+
|
|
59
|
+
const setWidget = useWidget.getReadonlyState().setWidget;
|
|
60
|
+
|
|
61
|
+
const contentBG = getContentBG(isAdded, isDelete, hasDiff);
|
|
62
|
+
|
|
63
|
+
const lineNumberBG = getLineNumberBG(isAdded, isDelete, hasDiff);
|
|
64
|
+
|
|
65
|
+
const plainLine = getCurrentPlainLine(currentLine.lineNumber);
|
|
66
|
+
|
|
67
|
+
const syntaxLine = getCurrentSyntaxLine(currentLine.lineNumber);
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<div
|
|
71
|
+
data-line={lineNumber}
|
|
72
|
+
data-state={hasDiff || !hasContent ? "diff" : "plain"}
|
|
73
|
+
data-side={SplitSide[side]}
|
|
74
|
+
className={"diff-line flex" + (hasContent ? " group" : "")}
|
|
75
|
+
>
|
|
76
|
+
{hasContent ? (
|
|
77
|
+
<>
|
|
78
|
+
<div
|
|
79
|
+
className={`diff-line-${SplitSide[side]}-num sticky left-0 flex w-[1%] min-w-[40px] select-none items-center px-[10px] text-right`}
|
|
80
|
+
style={{
|
|
81
|
+
backgroundColor: lineNumberBG,
|
|
82
|
+
color: `var(${hasDiff ? plainLineNumberColorName : expandLineNumberColorName})`,
|
|
83
|
+
width: `var(${diffAsideWidthName})`,
|
|
84
|
+
minWidth: `var(${diffAsideWidthName})`,
|
|
85
|
+
maxWidth: `var(${diffAsideWidthName})`,
|
|
86
|
+
}}
|
|
87
|
+
>
|
|
88
|
+
{hasDiff && enableAddWidget && (
|
|
89
|
+
<DiffSplitAddWidget
|
|
90
|
+
index={index}
|
|
91
|
+
lineNumber={currentLine.lineNumber}
|
|
92
|
+
side={side}
|
|
93
|
+
diffFile={diffFile}
|
|
94
|
+
onWidgetClick={(...props) => onAddWidgetClick.current?.(...props)}
|
|
95
|
+
className="absolute left-[100%] top-[50%] z-[1] translate-x-[-50%] translate-y-[-50%]"
|
|
96
|
+
onOpenAddWidget={(lineNumber, side) => setWidget({ lineNumber: lineNumber, side: side })}
|
|
97
|
+
/>
|
|
98
|
+
)}
|
|
99
|
+
<span
|
|
100
|
+
className="w-full"
|
|
101
|
+
data-line-num={currentLine.lineNumber}
|
|
102
|
+
style={{ opacity: hasChange ? undefined : 0.5 }}
|
|
103
|
+
>
|
|
104
|
+
{currentLine.lineNumber}
|
|
105
|
+
</span>
|
|
106
|
+
</div>
|
|
107
|
+
<div
|
|
108
|
+
className={`diff-line-${SplitSide[side]}-content flex w-full items-center pr-[10px]`}
|
|
109
|
+
style={{ backgroundColor: contentBG }}
|
|
110
|
+
>
|
|
111
|
+
<DiffContent
|
|
112
|
+
enableWrap={false}
|
|
113
|
+
diffFile={diffFile}
|
|
114
|
+
rawLine={currentLine.value!}
|
|
115
|
+
diffLine={currentLine.diff}
|
|
116
|
+
plainLine={plainLine}
|
|
117
|
+
syntaxLine={syntaxLine}
|
|
118
|
+
enableHighlight={enableHighlight}
|
|
119
|
+
/>
|
|
120
|
+
</div>
|
|
121
|
+
</>
|
|
122
|
+
) : (
|
|
123
|
+
<div
|
|
124
|
+
className={`diff-line-${SplitSide[side]}-placeholder w-full select-none`}
|
|
125
|
+
style={{ backgroundColor: `var(${emptyBGName})` }}
|
|
126
|
+
>
|
|
127
|
+
 
|
|
128
|
+
</div>
|
|
129
|
+
)}
|
|
130
|
+
</div>
|
|
131
|
+
);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export const DiffSplitContentLine = ({
|
|
135
|
+
index,
|
|
136
|
+
diffFile,
|
|
137
|
+
lineNumber,
|
|
138
|
+
side,
|
|
139
|
+
}: {
|
|
140
|
+
index: number;
|
|
141
|
+
side: SplitSide;
|
|
142
|
+
diffFile: DiffFile;
|
|
143
|
+
lineNumber: number;
|
|
144
|
+
}) => {
|
|
145
|
+
const getCurrentLine = side === SplitSide.old ? diffFile.getSplitLeftLine : diffFile.getSplitRightLine;
|
|
146
|
+
|
|
147
|
+
const currentLine = getCurrentLine(index);
|
|
148
|
+
|
|
149
|
+
if (currentLine?.isHidden) return null;
|
|
150
|
+
|
|
151
|
+
return <InternalDiffSplitLine index={index} diffFile={diffFile} lineNumber={lineNumber} side={side} />;
|
|
152
|
+
};
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { DiffLineType, type DiffFile, checkDiffLineIncludeChange } from "@git-diff-view/core";
|
|
2
|
+
import {
|
|
3
|
+
getContentBG,
|
|
4
|
+
getLineNumberBG,
|
|
5
|
+
plainLineNumberColorName,
|
|
6
|
+
emptyBGName,
|
|
7
|
+
borderColorName,
|
|
8
|
+
diffAsideWidthName,
|
|
9
|
+
expandLineNumberColorName,
|
|
10
|
+
} from "@git-diff-view/utils";
|
|
11
|
+
import * as React from "react";
|
|
12
|
+
|
|
13
|
+
import { DiffSplitAddWidget } from "../DiffAddWidget";
|
|
14
|
+
import { DiffContent } from "../DiffContent";
|
|
15
|
+
import { SplitSide } from "../DiffView";
|
|
16
|
+
import { useDiffViewContext } from "../DiffViewContext";
|
|
17
|
+
import { useDiffWidgetContext } from "../DiffWidgetContext";
|
|
18
|
+
|
|
19
|
+
const InternalDiffSplitLine = ({
|
|
20
|
+
index,
|
|
21
|
+
diffFile,
|
|
22
|
+
lineNumber,
|
|
23
|
+
}: {
|
|
24
|
+
index: number;
|
|
25
|
+
diffFile: DiffFile;
|
|
26
|
+
lineNumber: number;
|
|
27
|
+
}) => {
|
|
28
|
+
const oldLine = diffFile.getSplitLeftLine(index);
|
|
29
|
+
|
|
30
|
+
const newLine = diffFile.getSplitRightLine(index);
|
|
31
|
+
|
|
32
|
+
const oldSyntaxLine = diffFile.getOldSyntaxLine(oldLine?.lineNumber);
|
|
33
|
+
|
|
34
|
+
const newSyntaxLine = diffFile.getNewSyntaxLine(newLine?.lineNumber);
|
|
35
|
+
|
|
36
|
+
const oldPlainLine = diffFile.getOldPlainLine(oldLine.lineNumber);
|
|
37
|
+
|
|
38
|
+
const newPlainLine = diffFile.getNewPlainLine(newLine.lineNumber);
|
|
39
|
+
|
|
40
|
+
const hasDiff = !!oldLine?.diff || !!newLine?.diff;
|
|
41
|
+
|
|
42
|
+
const hasChange = checkDiffLineIncludeChange(oldLine?.diff) || checkDiffLineIncludeChange(newLine?.diff);
|
|
43
|
+
|
|
44
|
+
const oldLineIsDelete = oldLine?.diff?.type === DiffLineType.Delete;
|
|
45
|
+
|
|
46
|
+
const newLineIsAdded = newLine?.diff?.type === DiffLineType.Add;
|
|
47
|
+
|
|
48
|
+
const { useDiffContext } = useDiffViewContext();
|
|
49
|
+
|
|
50
|
+
const { enableHighlight, enableAddWidget, onAddWidgetClick } = useDiffContext.useShallowStableSelector((s) => ({
|
|
51
|
+
enableHighlight: s.enableHighlight,
|
|
52
|
+
enableAddWidget: s.enableAddWidget,
|
|
53
|
+
onAddWidgetClick: s.onAddWidgetClick,
|
|
54
|
+
}));
|
|
55
|
+
|
|
56
|
+
const { useWidget } = useDiffWidgetContext();
|
|
57
|
+
|
|
58
|
+
const setWidget = useWidget.getReadonlyState().setWidget;
|
|
59
|
+
|
|
60
|
+
const hasOldLine = !!oldLine.lineNumber;
|
|
61
|
+
|
|
62
|
+
const hasNewLine = !!newLine.lineNumber;
|
|
63
|
+
|
|
64
|
+
const oldLineContentBG = getContentBG(false, oldLineIsDelete, hasDiff);
|
|
65
|
+
|
|
66
|
+
const oldLineNumberBG = getLineNumberBG(false, oldLineIsDelete, hasDiff);
|
|
67
|
+
|
|
68
|
+
const newLineContentBG = getContentBG(newLineIsAdded, false, hasDiff);
|
|
69
|
+
|
|
70
|
+
const newLineNumberBG = getLineNumberBG(newLineIsAdded, false, hasDiff);
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<div data-line={lineNumber} data-state={hasDiff ? "diff" : "plain"} className="diff-line flex">
|
|
74
|
+
{hasOldLine ? (
|
|
75
|
+
<>
|
|
76
|
+
<div
|
|
77
|
+
className="diff-line-old-num group relative flex w-[1%] min-w-[40px] select-none items-start px-[10px] text-right"
|
|
78
|
+
data-side={SplitSide[SplitSide.old]}
|
|
79
|
+
style={{
|
|
80
|
+
backgroundColor: oldLineNumberBG,
|
|
81
|
+
color: `var(${hasDiff ? plainLineNumberColorName : expandLineNumberColorName})`,
|
|
82
|
+
width: `var(${diffAsideWidthName})`,
|
|
83
|
+
minWidth: `var(${diffAsideWidthName})`,
|
|
84
|
+
maxWidth: `var(${diffAsideWidthName})`,
|
|
85
|
+
}}
|
|
86
|
+
>
|
|
87
|
+
{hasDiff && enableAddWidget && (
|
|
88
|
+
<DiffSplitAddWidget
|
|
89
|
+
index={index}
|
|
90
|
+
lineNumber={oldLine.lineNumber}
|
|
91
|
+
side={SplitSide.old}
|
|
92
|
+
diffFile={diffFile}
|
|
93
|
+
onWidgetClick={(...props) => onAddWidgetClick.current?.(...props)}
|
|
94
|
+
className="absolute left-[100%] z-[1] translate-x-[-50%]"
|
|
95
|
+
onOpenAddWidget={(lineNumber, side) => setWidget({ lineNumber: lineNumber, side: side })}
|
|
96
|
+
/>
|
|
97
|
+
)}
|
|
98
|
+
<span
|
|
99
|
+
className="w-full"
|
|
100
|
+
data-line-num={oldLine.lineNumber}
|
|
101
|
+
style={{ opacity: hasChange ? undefined : 0.5 }}
|
|
102
|
+
>
|
|
103
|
+
{oldLine.lineNumber}
|
|
104
|
+
</span>
|
|
105
|
+
</div>
|
|
106
|
+
<div
|
|
107
|
+
className="diff-line-old-content group relative flex w-[50%] items-center pr-[10px]"
|
|
108
|
+
data-side={SplitSide[SplitSide.old]}
|
|
109
|
+
style={{ backgroundColor: oldLineContentBG }}
|
|
110
|
+
>
|
|
111
|
+
{hasDiff && enableAddWidget && (
|
|
112
|
+
<DiffSplitAddWidget
|
|
113
|
+
index={index}
|
|
114
|
+
lineNumber={oldLine.lineNumber}
|
|
115
|
+
side={SplitSide.old}
|
|
116
|
+
diffFile={diffFile}
|
|
117
|
+
onWidgetClick={(...props) => onAddWidgetClick.current?.(...props)}
|
|
118
|
+
className="absolute right-[100%] top-0 z-[1] translate-x-[50%]"
|
|
119
|
+
onOpenAddWidget={(lineNumber, side) => setWidget({ lineNumber: lineNumber, side: side })}
|
|
120
|
+
/>
|
|
121
|
+
)}
|
|
122
|
+
<DiffContent
|
|
123
|
+
enableWrap={true}
|
|
124
|
+
diffFile={diffFile}
|
|
125
|
+
rawLine={oldLine.value}
|
|
126
|
+
diffLine={oldLine.diff}
|
|
127
|
+
plainLine={oldPlainLine}
|
|
128
|
+
syntaxLine={oldSyntaxLine}
|
|
129
|
+
enableHighlight={enableHighlight}
|
|
130
|
+
/>
|
|
131
|
+
</div>
|
|
132
|
+
</>
|
|
133
|
+
) : (
|
|
134
|
+
<>
|
|
135
|
+
<div
|
|
136
|
+
className="diff-line-old-num-placeholder w-[1%] min-w-[40px] select-none px-[10px]"
|
|
137
|
+
data-side={SplitSide[SplitSide.old]}
|
|
138
|
+
style={{
|
|
139
|
+
backgroundColor: `var(${emptyBGName})`,
|
|
140
|
+
width: `var(${diffAsideWidthName})`,
|
|
141
|
+
minWidth: `var(${diffAsideWidthName})`,
|
|
142
|
+
maxWidth: `var(${diffAsideWidthName})`,
|
|
143
|
+
}}
|
|
144
|
+
>
|
|
145
|
+
 
|
|
146
|
+
</div>
|
|
147
|
+
<div
|
|
148
|
+
className="diff-line-old-placeholder w-[50%] select-none pr-[10px]"
|
|
149
|
+
data-side={SplitSide[SplitSide.old]}
|
|
150
|
+
style={{ backgroundColor: `var(${emptyBGName})` }}
|
|
151
|
+
>
|
|
152
|
+
 
|
|
153
|
+
</div>
|
|
154
|
+
</>
|
|
155
|
+
)}
|
|
156
|
+
<div className="diff-split-line w-[1px] flex-shrink-0" style={{ backgroundColor: `var(${borderColorName})` }} />
|
|
157
|
+
{hasNewLine ? (
|
|
158
|
+
<>
|
|
159
|
+
<div
|
|
160
|
+
className="diff-line-new-num group relative flex w-[1%] min-w-[40px] select-none items-start px-[10px] text-right"
|
|
161
|
+
data-side={SplitSide[SplitSide.new]}
|
|
162
|
+
style={{
|
|
163
|
+
backgroundColor: newLineNumberBG,
|
|
164
|
+
color: `var(${hasDiff ? plainLineNumberColorName : expandLineNumberColorName})`,
|
|
165
|
+
width: `var(${diffAsideWidthName})`,
|
|
166
|
+
minWidth: `var(${diffAsideWidthName})`,
|
|
167
|
+
maxWidth: `var(${diffAsideWidthName})`,
|
|
168
|
+
}}
|
|
169
|
+
>
|
|
170
|
+
{hasDiff && enableAddWidget && (
|
|
171
|
+
<DiffSplitAddWidget
|
|
172
|
+
index={index}
|
|
173
|
+
lineNumber={newLine.lineNumber}
|
|
174
|
+
side={SplitSide.new}
|
|
175
|
+
diffFile={diffFile}
|
|
176
|
+
onWidgetClick={(...props) => onAddWidgetClick.current?.(...props)}
|
|
177
|
+
className="absolute left-[100%] z-[1] translate-x-[-50%]"
|
|
178
|
+
onOpenAddWidget={(lineNumber, side) => setWidget({ lineNumber: lineNumber, side: side })}
|
|
179
|
+
/>
|
|
180
|
+
)}
|
|
181
|
+
<span
|
|
182
|
+
className="w-full"
|
|
183
|
+
data-line-num={newLine.lineNumber}
|
|
184
|
+
style={{ opacity: hasChange ? undefined : 0.5 }}
|
|
185
|
+
>
|
|
186
|
+
{newLine.lineNumber}
|
|
187
|
+
</span>
|
|
188
|
+
</div>
|
|
189
|
+
<div
|
|
190
|
+
className="diff-line-new-content group relative flex w-[50%] items-center pr-[10px]"
|
|
191
|
+
data-side={SplitSide[SplitSide.new]}
|
|
192
|
+
style={{ backgroundColor: newLineContentBG }}
|
|
193
|
+
>
|
|
194
|
+
{hasDiff && enableAddWidget && (
|
|
195
|
+
<DiffSplitAddWidget
|
|
196
|
+
index={index}
|
|
197
|
+
lineNumber={newLine.lineNumber}
|
|
198
|
+
side={SplitSide.new}
|
|
199
|
+
diffFile={diffFile}
|
|
200
|
+
onWidgetClick={(...props) => onAddWidgetClick.current?.(...props)}
|
|
201
|
+
className="absolute right-[100%] top-0 z-[1] translate-x-[50%]"
|
|
202
|
+
onOpenAddWidget={(lineNumber, side) => setWidget({ lineNumber: lineNumber, side: side })}
|
|
203
|
+
/>
|
|
204
|
+
)}
|
|
205
|
+
<DiffContent
|
|
206
|
+
enableWrap={true}
|
|
207
|
+
diffFile={diffFile}
|
|
208
|
+
rawLine={newLine.value || ""}
|
|
209
|
+
diffLine={newLine.diff}
|
|
210
|
+
plainLine={newPlainLine}
|
|
211
|
+
syntaxLine={newSyntaxLine}
|
|
212
|
+
enableHighlight={enableHighlight}
|
|
213
|
+
/>
|
|
214
|
+
</div>
|
|
215
|
+
</>
|
|
216
|
+
) : (
|
|
217
|
+
<>
|
|
218
|
+
<div
|
|
219
|
+
className="diff-line-new-num-placeholder w-[1%] min-w-[40px] select-none px-[10px]"
|
|
220
|
+
data-side={SplitSide[SplitSide.new]}
|
|
221
|
+
style={{
|
|
222
|
+
backgroundColor: `var(${emptyBGName})`,
|
|
223
|
+
width: `var(${diffAsideWidthName})`,
|
|
224
|
+
minWidth: `var(${diffAsideWidthName})`,
|
|
225
|
+
maxWidth: `var(${diffAsideWidthName})`,
|
|
226
|
+
}}
|
|
227
|
+
>
|
|
228
|
+
 
|
|
229
|
+
</div>
|
|
230
|
+
<div
|
|
231
|
+
className="diff-line-new-placeholder w-[50%] select-none pr-[10px]"
|
|
232
|
+
data-side={SplitSide[SplitSide.new]}
|
|
233
|
+
style={{ backgroundColor: `var(${emptyBGName})` }}
|
|
234
|
+
>
|
|
235
|
+
 
|
|
236
|
+
</div>
|
|
237
|
+
</>
|
|
238
|
+
)}
|
|
239
|
+
</div>
|
|
240
|
+
);
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
export const DiffSplitContentLine = ({
|
|
244
|
+
index,
|
|
245
|
+
diffFile,
|
|
246
|
+
lineNumber,
|
|
247
|
+
}: {
|
|
248
|
+
index: number;
|
|
249
|
+
diffFile: DiffFile;
|
|
250
|
+
lineNumber: number;
|
|
251
|
+
}) => {
|
|
252
|
+
const oldLine = diffFile.getSplitLeftLine(index);
|
|
253
|
+
|
|
254
|
+
const newLine = diffFile.getSplitRightLine(index);
|
|
255
|
+
|
|
256
|
+
if (oldLine?.isHidden && newLine?.isHidden) return null;
|
|
257
|
+
|
|
258
|
+
return <InternalDiffSplitLine index={index} diffFile={diffFile} lineNumber={lineNumber} />;
|
|
259
|
+
};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { emptyBGName } from "@git-diff-view/utils";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
import { useDomWidth } from "../../hooks/useDomWidth";
|
|
5
|
+
import { useSyncHeight } from "../../hooks/useSyncHeight";
|
|
6
|
+
import { SplitSide } from "../DiffView";
|
|
7
|
+
import { useDiffViewContext } from "../DiffViewContext";
|
|
8
|
+
|
|
9
|
+
import type { DiffFile } from "@git-diff-view/core";
|
|
10
|
+
|
|
11
|
+
const InternalDiffSplitExtendLine = ({
|
|
12
|
+
index,
|
|
13
|
+
diffFile,
|
|
14
|
+
oldLineExtend,
|
|
15
|
+
newLineExtend,
|
|
16
|
+
side,
|
|
17
|
+
lineNumber,
|
|
18
|
+
}: {
|
|
19
|
+
index: number;
|
|
20
|
+
side: SplitSide;
|
|
21
|
+
oldLineExtend: { data: any };
|
|
22
|
+
newLineExtend: { data: any };
|
|
23
|
+
diffFile: DiffFile;
|
|
24
|
+
lineNumber: number;
|
|
25
|
+
}) => {
|
|
26
|
+
const { useDiffContext } = useDiffViewContext();
|
|
27
|
+
|
|
28
|
+
const oldLine = diffFile.getSplitLeftLine(index);
|
|
29
|
+
|
|
30
|
+
const newLine = diffFile.getSplitRightLine(index);
|
|
31
|
+
|
|
32
|
+
const renderExtendLine = useDiffContext.useShallowStableSelector((s) => s.renderExtendLine);
|
|
33
|
+
|
|
34
|
+
const currentExtend = side === SplitSide.old ? oldLineExtend : newLineExtend;
|
|
35
|
+
|
|
36
|
+
const otherSide = side === SplitSide.old ? SplitSide.new : SplitSide.old;
|
|
37
|
+
|
|
38
|
+
const currentLineNumber = side === SplitSide.old ? oldLine.lineNumber : newLine.lineNumber;
|
|
39
|
+
|
|
40
|
+
const currentSideHasExtend = currentExtend?.data !== undefined && currentExtend?.data !== null;
|
|
41
|
+
|
|
42
|
+
const hasExtend =
|
|
43
|
+
(oldLineExtend?.data !== undefined && oldLineExtend?.data !== null) ||
|
|
44
|
+
(newLineExtend?.data !== undefined && newLineExtend?.data !== null);
|
|
45
|
+
|
|
46
|
+
useSyncHeight({
|
|
47
|
+
wrapper: `div[data-state="extend"][data-line="${lineNumber}-extend"]`,
|
|
48
|
+
selector: `div[data-line="${lineNumber}-extend-content"]`,
|
|
49
|
+
side: SplitSide[currentSideHasExtend ? side : otherSide],
|
|
50
|
+
enable: hasExtend && typeof renderExtendLine === "function",
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const width = useDomWidth({
|
|
54
|
+
selector: side === SplitSide.old ? ".old-diff-table-wrapper" : ".new-diff-table-wrapper",
|
|
55
|
+
enable: currentSideHasExtend && typeof renderExtendLine === "function",
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
if (!renderExtendLine) return null;
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<div
|
|
62
|
+
data-line={`${lineNumber}-extend`}
|
|
63
|
+
data-state="extend"
|
|
64
|
+
data-side={SplitSide[side]}
|
|
65
|
+
className="diff-line diff-line-extend"
|
|
66
|
+
>
|
|
67
|
+
{currentSideHasExtend ? (
|
|
68
|
+
<div className={`diff-line-extend-${SplitSide[side]}-content p-0`}>
|
|
69
|
+
<div
|
|
70
|
+
data-line={`${lineNumber}-extend-content`}
|
|
71
|
+
data-side={SplitSide[side]}
|
|
72
|
+
className="diff-line-extend-wrapper sticky left-0 z-[1]"
|
|
73
|
+
style={{ width }}
|
|
74
|
+
>
|
|
75
|
+
{width > 0 &&
|
|
76
|
+
hasExtend &&
|
|
77
|
+
renderExtendLine?.({
|
|
78
|
+
diffFile,
|
|
79
|
+
side,
|
|
80
|
+
lineNumber: currentLineNumber,
|
|
81
|
+
data: currentExtend?.data,
|
|
82
|
+
onUpdate: diffFile.notifyAll,
|
|
83
|
+
})}
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
) : (
|
|
87
|
+
<div
|
|
88
|
+
className={`diff-line-extend-${SplitSide[side]}-placeholder h-full select-none p-0`}
|
|
89
|
+
style={{ backgroundColor: `var(${emptyBGName})` }}
|
|
90
|
+
>
|
|
91
|
+
<div data-line={`${lineNumber}-extend-content`} data-side={SplitSide[side]} />
|
|
92
|
+
</div>
|
|
93
|
+
)}
|
|
94
|
+
</div>
|
|
95
|
+
);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export const DiffSplitExtendLine = ({
|
|
99
|
+
index,
|
|
100
|
+
diffFile,
|
|
101
|
+
side,
|
|
102
|
+
lineNumber,
|
|
103
|
+
}: {
|
|
104
|
+
index: number;
|
|
105
|
+
side: SplitSide;
|
|
106
|
+
diffFile: DiffFile;
|
|
107
|
+
lineNumber: number;
|
|
108
|
+
}) => {
|
|
109
|
+
const { useDiffContext } = useDiffViewContext();
|
|
110
|
+
|
|
111
|
+
const oldLine = diffFile.getSplitLeftLine(index);
|
|
112
|
+
|
|
113
|
+
const newLine = diffFile.getSplitRightLine(index);
|
|
114
|
+
|
|
115
|
+
const { oldLineExtend, newLineExtend } = useDiffContext(
|
|
116
|
+
React.useCallback(
|
|
117
|
+
(s) => ({
|
|
118
|
+
oldLineExtend: s.extendData?.oldFile?.[oldLine?.lineNumber],
|
|
119
|
+
newLineExtend: s.extendData?.newFile?.[newLine?.lineNumber],
|
|
120
|
+
}),
|
|
121
|
+
[oldLine?.lineNumber, newLine?.lineNumber]
|
|
122
|
+
)
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
const hasExtend = oldLineExtend?.data || newLineExtend?.data;
|
|
126
|
+
|
|
127
|
+
// if the expand action not enabled, the `isHidden` property will never change
|
|
128
|
+
const enableExpand = diffFile.getExpandEnabled();
|
|
129
|
+
|
|
130
|
+
const currentLine = side === SplitSide.old ? oldLine : newLine;
|
|
131
|
+
|
|
132
|
+
const currentIsShow = hasExtend && (!currentLine.isHidden || !enableExpand);
|
|
133
|
+
|
|
134
|
+
if (!currentIsShow) return null;
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
<InternalDiffSplitExtendLine
|
|
138
|
+
side={side}
|
|
139
|
+
index={index}
|
|
140
|
+
diffFile={diffFile}
|
|
141
|
+
lineNumber={lineNumber}
|
|
142
|
+
oldLineExtend={oldLineExtend}
|
|
143
|
+
newLineExtend={newLineExtend}
|
|
144
|
+
/>
|
|
145
|
+
);
|
|
146
|
+
};
|