@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,326 @@
|
|
|
1
|
+
import { composeLen, type DiffFile } from "@git-diff-view/core";
|
|
2
|
+
import {
|
|
3
|
+
diffAsideWidthName,
|
|
4
|
+
hunkLineNumberBGName,
|
|
5
|
+
plainLineNumberColorName,
|
|
6
|
+
hunkContentBGName,
|
|
7
|
+
hunkContentColorName,
|
|
8
|
+
borderColorName,
|
|
9
|
+
} from "@git-diff-view/utils";
|
|
10
|
+
import * as React from "react";
|
|
11
|
+
|
|
12
|
+
import { ExpandUp, ExpandDown, ExpandAll } from "../DiffExpand";
|
|
13
|
+
import { DiffModeEnum } from "../DiffView";
|
|
14
|
+
import { useDiffViewContext } from "../DiffViewContext";
|
|
15
|
+
|
|
16
|
+
const DiffSplitHunkLineGitHub = ({
|
|
17
|
+
index,
|
|
18
|
+
diffFile,
|
|
19
|
+
lineNumber,
|
|
20
|
+
}: {
|
|
21
|
+
index: number;
|
|
22
|
+
diffFile: DiffFile;
|
|
23
|
+
lineNumber: number;
|
|
24
|
+
}) => {
|
|
25
|
+
const currentHunk = diffFile.getSplitHunkLine(index);
|
|
26
|
+
|
|
27
|
+
const expandEnabled = diffFile.getExpandEnabled();
|
|
28
|
+
|
|
29
|
+
const couldExpand = expandEnabled && currentHunk && currentHunk.splitInfo;
|
|
30
|
+
|
|
31
|
+
const isExpandAll =
|
|
32
|
+
currentHunk &&
|
|
33
|
+
currentHunk.splitInfo &&
|
|
34
|
+
currentHunk.splitInfo.endHiddenIndex - currentHunk.splitInfo.startHiddenIndex < composeLen;
|
|
35
|
+
|
|
36
|
+
const isFirstLine = currentHunk && currentHunk.isFirst;
|
|
37
|
+
|
|
38
|
+
const isLastLine = currentHunk && currentHunk.isLast;
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<div data-line={`${lineNumber}-hunk`} data-state="hunk" className="diff-line diff-line-hunk flex">
|
|
42
|
+
<div
|
|
43
|
+
className="diff-line-hunk-action flex w-[1%] min-w-[40px] select-none flex-col items-center justify-center p-[1px]"
|
|
44
|
+
style={{
|
|
45
|
+
backgroundColor: `var(${hunkLineNumberBGName})`,
|
|
46
|
+
color: `var(${plainLineNumberColorName})`,
|
|
47
|
+
width: `var(${diffAsideWidthName})`,
|
|
48
|
+
minWidth: `var(${diffAsideWidthName})`,
|
|
49
|
+
maxWidth: `var(${diffAsideWidthName})`,
|
|
50
|
+
}}
|
|
51
|
+
>
|
|
52
|
+
{couldExpand ? (
|
|
53
|
+
isFirstLine ? (
|
|
54
|
+
<button
|
|
55
|
+
className="diff-widget-tooltip flex w-full cursor-pointer items-center justify-center rounded-[2px] py-[6px]"
|
|
56
|
+
title="Expand Up"
|
|
57
|
+
data-title="Expand Up"
|
|
58
|
+
onClick={() => diffFile.onSplitHunkExpand("up", index)}
|
|
59
|
+
>
|
|
60
|
+
<ExpandUp className="fill-current" />
|
|
61
|
+
</button>
|
|
62
|
+
) : isLastLine ? (
|
|
63
|
+
<button
|
|
64
|
+
className="diff-widget-tooltip flex w-full cursor-pointer items-center justify-center rounded-[2px] py-[6px]"
|
|
65
|
+
title="Expand Down"
|
|
66
|
+
data-title="Expand Down"
|
|
67
|
+
onClick={() => diffFile.onSplitHunkExpand("down", index)}
|
|
68
|
+
>
|
|
69
|
+
<ExpandDown className="fill-current" />
|
|
70
|
+
</button>
|
|
71
|
+
) : isExpandAll ? (
|
|
72
|
+
<button
|
|
73
|
+
className="diff-widget-tooltip flex w-full cursor-pointer items-center justify-center rounded-[2px] py-[6px]"
|
|
74
|
+
title="Expand All"
|
|
75
|
+
data-title="Expand All"
|
|
76
|
+
onClick={() => diffFile.onSplitHunkExpand("all", index)}
|
|
77
|
+
>
|
|
78
|
+
<ExpandAll className="fill-current" />
|
|
79
|
+
</button>
|
|
80
|
+
) : (
|
|
81
|
+
<>
|
|
82
|
+
<button
|
|
83
|
+
className="diff-widget-tooltip flex w-full cursor-pointer items-center justify-center rounded-[2px] py-[2px]"
|
|
84
|
+
title="Expand Down"
|
|
85
|
+
data-title="Expand Down"
|
|
86
|
+
onClick={() => diffFile.onSplitHunkExpand("down", index)}
|
|
87
|
+
>
|
|
88
|
+
<ExpandDown className="fill-current" />
|
|
89
|
+
</button>
|
|
90
|
+
<button
|
|
91
|
+
className="diff-widget-tooltip flex w-full cursor-pointer items-center justify-center rounded-[2px] py-[2px]"
|
|
92
|
+
title="Expand Up"
|
|
93
|
+
data-title="Expand Up"
|
|
94
|
+
onClick={() => diffFile.onSplitHunkExpand("up", index)}
|
|
95
|
+
>
|
|
96
|
+
<ExpandUp className="fill-current" />
|
|
97
|
+
</button>
|
|
98
|
+
</>
|
|
99
|
+
)
|
|
100
|
+
) : (
|
|
101
|
+
<div className="min-h-[28px]"> </div>
|
|
102
|
+
)}
|
|
103
|
+
</div>
|
|
104
|
+
<div
|
|
105
|
+
className="diff-line-hunk-content flex w-full items-center px-[10px]"
|
|
106
|
+
style={{ backgroundColor: `var(${hunkContentBGName})`, color: `var(${hunkContentColorName})` }}
|
|
107
|
+
>
|
|
108
|
+
{currentHunk.splitInfo?.plainText || currentHunk.text}
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const DiffSplitHunkLineGitLab = ({
|
|
115
|
+
index,
|
|
116
|
+
diffFile,
|
|
117
|
+
lineNumber,
|
|
118
|
+
}: {
|
|
119
|
+
index: number;
|
|
120
|
+
diffFile: DiffFile;
|
|
121
|
+
lineNumber: number;
|
|
122
|
+
}) => {
|
|
123
|
+
const currentHunk = diffFile.getSplitHunkLine(index);
|
|
124
|
+
|
|
125
|
+
const expandEnabled = diffFile.getExpandEnabled();
|
|
126
|
+
|
|
127
|
+
const couldExpand = expandEnabled && currentHunk && currentHunk.splitInfo;
|
|
128
|
+
|
|
129
|
+
const isExpandAll =
|
|
130
|
+
currentHunk &&
|
|
131
|
+
currentHunk.splitInfo &&
|
|
132
|
+
currentHunk.splitInfo.endHiddenIndex - currentHunk.splitInfo.startHiddenIndex < composeLen;
|
|
133
|
+
|
|
134
|
+
const isFirstLine = currentHunk && currentHunk.isFirst;
|
|
135
|
+
|
|
136
|
+
const isLastLine = currentHunk && currentHunk.isLast;
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
<div data-line={`${lineNumber}-hunk`} data-state="hunk" className="diff-line diff-line-hunk flex">
|
|
140
|
+
<div
|
|
141
|
+
className="diff-line-hunk-action flex w-[1%] min-w-[40px] select-none flex-col items-center justify-center p-[1px]"
|
|
142
|
+
style={{
|
|
143
|
+
backgroundColor: `var(${hunkLineNumberBGName})`,
|
|
144
|
+
color: `var(${plainLineNumberColorName})`,
|
|
145
|
+
width: `var(${diffAsideWidthName})`,
|
|
146
|
+
minWidth: `var(${diffAsideWidthName})`,
|
|
147
|
+
maxWidth: `var(${diffAsideWidthName})`,
|
|
148
|
+
}}
|
|
149
|
+
>
|
|
150
|
+
{couldExpand ? (
|
|
151
|
+
isFirstLine ? (
|
|
152
|
+
<button
|
|
153
|
+
className="diff-widget-tooltip flex h-full w-full cursor-pointer items-center justify-center rounded-[2px] py-[6px]"
|
|
154
|
+
title="Expand Up"
|
|
155
|
+
data-title="Expand Up"
|
|
156
|
+
onClick={() => diffFile.onSplitHunkExpand("up", index)}
|
|
157
|
+
>
|
|
158
|
+
<ExpandUp className="fill-current" />
|
|
159
|
+
</button>
|
|
160
|
+
) : isLastLine ? (
|
|
161
|
+
<button
|
|
162
|
+
className="diff-widget-tooltip flex h-full w-full cursor-pointer items-center justify-center rounded-[2px] py-[6px]"
|
|
163
|
+
title="Expand Down"
|
|
164
|
+
data-title="Expand Down"
|
|
165
|
+
onClick={() => diffFile.onSplitHunkExpand("down", index)}
|
|
166
|
+
>
|
|
167
|
+
<ExpandDown className="fill-current" />
|
|
168
|
+
</button>
|
|
169
|
+
) : isExpandAll ? (
|
|
170
|
+
<button
|
|
171
|
+
className="diff-widget-tooltip flex h-full w-full cursor-pointer items-center justify-center rounded-[2px] py-[6px]"
|
|
172
|
+
title="Expand All"
|
|
173
|
+
data-title="Expand All"
|
|
174
|
+
onClick={() => diffFile.onSplitHunkExpand("all", index)}
|
|
175
|
+
>
|
|
176
|
+
<ExpandAll className="fill-current" />
|
|
177
|
+
</button>
|
|
178
|
+
) : (
|
|
179
|
+
<>
|
|
180
|
+
<button
|
|
181
|
+
className="diff-widget-tooltip flex h-[50%] w-full cursor-pointer items-center justify-center rounded-[2px] py-[2px]"
|
|
182
|
+
title="Expand Down"
|
|
183
|
+
data-title="Expand Down"
|
|
184
|
+
onClick={() => diffFile.onSplitHunkExpand("down", index)}
|
|
185
|
+
>
|
|
186
|
+
<ExpandDown className="fill-current" />
|
|
187
|
+
</button>
|
|
188
|
+
<button
|
|
189
|
+
className="diff-widget-tooltip flex h-[50%] w-full cursor-pointer items-center justify-center rounded-[2px] py-[2px]"
|
|
190
|
+
title="Expand Up"
|
|
191
|
+
data-title="Expand Up"
|
|
192
|
+
onClick={() => diffFile.onSplitHunkExpand("up", index)}
|
|
193
|
+
>
|
|
194
|
+
<ExpandUp className="fill-current" />
|
|
195
|
+
</button>
|
|
196
|
+
</>
|
|
197
|
+
)
|
|
198
|
+
) : (
|
|
199
|
+
<div className="min-h-[28px]"> </div>
|
|
200
|
+
)}
|
|
201
|
+
</div>
|
|
202
|
+
<div
|
|
203
|
+
className="diff-line-hunk-content flex w-full items-center px-[10px]"
|
|
204
|
+
style={{ backgroundColor: `var(${hunkContentBGName})`, color: `var(${hunkContentColorName})` }}
|
|
205
|
+
>
|
|
206
|
+
{currentHunk.splitInfo?.plainText || currentHunk.text}
|
|
207
|
+
</div>
|
|
208
|
+
<div className="diff-split-line w-[1px] flex-shrink-0" style={{ backgroundColor: `var(${borderColorName})` }} />
|
|
209
|
+
<div
|
|
210
|
+
className="diff-line-hunk-action relative z-[1] flex w-[1%] min-w-[40px] select-none flex-col items-center justify-center p-[1px]"
|
|
211
|
+
style={{
|
|
212
|
+
backgroundColor: `var(${hunkLineNumberBGName})`,
|
|
213
|
+
color: `var(${plainLineNumberColorName})`,
|
|
214
|
+
width: `var(${diffAsideWidthName})`,
|
|
215
|
+
minWidth: `var(${diffAsideWidthName})`,
|
|
216
|
+
maxWidth: `var(${diffAsideWidthName})`,
|
|
217
|
+
}}
|
|
218
|
+
>
|
|
219
|
+
{couldExpand ? (
|
|
220
|
+
isFirstLine ? (
|
|
221
|
+
<button
|
|
222
|
+
className="diff-widget-tooltip flex h-full w-full cursor-pointer items-center justify-center rounded-[2px] py-[6px]"
|
|
223
|
+
title="Expand Up"
|
|
224
|
+
data-title="Expand Up"
|
|
225
|
+
onClick={() => diffFile.onSplitHunkExpand("up", index)}
|
|
226
|
+
>
|
|
227
|
+
<ExpandUp className="fill-current" />
|
|
228
|
+
</button>
|
|
229
|
+
) : isLastLine ? (
|
|
230
|
+
<button
|
|
231
|
+
className="diff-widget-tooltip flex h-full w-full cursor-pointer items-center justify-center rounded-[2px] py-[6px]"
|
|
232
|
+
title="Expand Down"
|
|
233
|
+
data-title="Expand Down"
|
|
234
|
+
onClick={() => diffFile.onSplitHunkExpand("down", index)}
|
|
235
|
+
>
|
|
236
|
+
<ExpandDown className="fill-current" />
|
|
237
|
+
</button>
|
|
238
|
+
) : isExpandAll ? (
|
|
239
|
+
<button
|
|
240
|
+
className="diff-widget-tooltip flex h-full w-full cursor-pointer items-center justify-center rounded-[2px] py-[6px]"
|
|
241
|
+
title="Expand All"
|
|
242
|
+
data-title="Expand All"
|
|
243
|
+
onClick={() => diffFile.onSplitHunkExpand("all", index)}
|
|
244
|
+
>
|
|
245
|
+
<ExpandAll className="fill-current" />
|
|
246
|
+
</button>
|
|
247
|
+
) : (
|
|
248
|
+
<>
|
|
249
|
+
<button
|
|
250
|
+
className="diff-widget-tooltip flex h-[50%] w-full cursor-pointer items-center justify-center rounded-[2px] py-[2px]"
|
|
251
|
+
title="Expand Down"
|
|
252
|
+
data-title="Expand Down"
|
|
253
|
+
onClick={() => diffFile.onSplitHunkExpand("down", index)}
|
|
254
|
+
>
|
|
255
|
+
<ExpandDown className="fill-current" />
|
|
256
|
+
</button>
|
|
257
|
+
<button
|
|
258
|
+
className="diff-widget-tooltip flex h-[50%] w-full cursor-pointer items-center justify-center rounded-[2px] py-[2px]"
|
|
259
|
+
title="Expand Up"
|
|
260
|
+
data-title="Expand Up"
|
|
261
|
+
onClick={() => diffFile.onSplitHunkExpand("up", index)}
|
|
262
|
+
>
|
|
263
|
+
<ExpandUp className="fill-current" />
|
|
264
|
+
</button>
|
|
265
|
+
</>
|
|
266
|
+
)
|
|
267
|
+
) : (
|
|
268
|
+
<div className="min-h-[28px]"> </div>
|
|
269
|
+
)}
|
|
270
|
+
</div>
|
|
271
|
+
<div
|
|
272
|
+
className="diff-line-hunk-content relative flex w-full items-center px-[10px]"
|
|
273
|
+
style={{ backgroundColor: `var(${hunkContentBGName})`, color: `var(${hunkContentColorName})` }}
|
|
274
|
+
>
|
|
275
|
+
{currentHunk.splitInfo?.plainText || currentHunk.text}
|
|
276
|
+
</div>
|
|
277
|
+
</div>
|
|
278
|
+
);
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
const InternalDiffSplitHunkLine = ({
|
|
282
|
+
index,
|
|
283
|
+
diffFile,
|
|
284
|
+
lineNumber,
|
|
285
|
+
}: {
|
|
286
|
+
index: number;
|
|
287
|
+
diffFile: DiffFile;
|
|
288
|
+
lineNumber: number;
|
|
289
|
+
}) => {
|
|
290
|
+
const { useDiffContext } = useDiffViewContext();
|
|
291
|
+
|
|
292
|
+
const diffViewMode = useDiffContext.useShallowStableSelector((s) => s.mode);
|
|
293
|
+
|
|
294
|
+
if (
|
|
295
|
+
diffViewMode === DiffModeEnum.SplitGitHub ||
|
|
296
|
+
diffViewMode === DiffModeEnum.Split ||
|
|
297
|
+
diffViewMode === DiffModeEnum.Unified
|
|
298
|
+
) {
|
|
299
|
+
return <DiffSplitHunkLineGitHub index={index} diffFile={diffFile} lineNumber={lineNumber} />;
|
|
300
|
+
} else {
|
|
301
|
+
return <DiffSplitHunkLineGitLab index={index} diffFile={diffFile} lineNumber={lineNumber} />;
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
export const DiffSplitHunkLine = ({
|
|
306
|
+
index,
|
|
307
|
+
diffFile,
|
|
308
|
+
lineNumber,
|
|
309
|
+
}: {
|
|
310
|
+
index: number;
|
|
311
|
+
diffFile: DiffFile;
|
|
312
|
+
lineNumber: number;
|
|
313
|
+
}) => {
|
|
314
|
+
const currentHunk = diffFile.getSplitHunkLine(index);
|
|
315
|
+
|
|
316
|
+
const currentIsShow =
|
|
317
|
+
currentHunk &&
|
|
318
|
+
currentHunk.splitInfo &&
|
|
319
|
+
currentHunk.splitInfo.startHiddenIndex < currentHunk.splitInfo.endHiddenIndex;
|
|
320
|
+
|
|
321
|
+
const currentIsPureHunk = currentHunk && diffFile._getIsPureDiffRender() && !currentHunk.splitInfo;
|
|
322
|
+
|
|
323
|
+
if (!currentIsShow && !currentIsPureHunk) return null;
|
|
324
|
+
|
|
325
|
+
return <InternalDiffSplitHunkLine index={index} diffFile={diffFile} lineNumber={lineNumber} />;
|
|
326
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { DiffFileLineType } from "@git-diff-view/core";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
import { DiffSplitContentLine } from "./DiffSplitContentLineNormal_v2";
|
|
5
|
+
import { DiffSplitExtendLine } from "./DiffSplitExtendLineNormal_v2";
|
|
6
|
+
import { DiffSplitHunkLine } from "./DiffSplitHunkLineNormal_v2";
|
|
7
|
+
import { DiffSplitWidgetLine } from "./DiffSplitWidgetLineNormal_v2";
|
|
8
|
+
|
|
9
|
+
import type { SplitSide } from "../DiffView";
|
|
10
|
+
import type { DiffFile, DiffSplitLineItem } from "@git-diff-view/core";
|
|
11
|
+
|
|
12
|
+
export const DiffSplitViewLine = ({
|
|
13
|
+
line,
|
|
14
|
+
side,
|
|
15
|
+
diffFile,
|
|
16
|
+
}: {
|
|
17
|
+
line: DiffSplitLineItem;
|
|
18
|
+
side: SplitSide;
|
|
19
|
+
diffFile: DiffFile;
|
|
20
|
+
}) => {
|
|
21
|
+
switch (line.type) {
|
|
22
|
+
case DiffFileLineType.hunk:
|
|
23
|
+
return <DiffSplitHunkLine side={side} index={line.index} lineNumber={line.lineNumber} diffFile={diffFile} />;
|
|
24
|
+
case DiffFileLineType.content:
|
|
25
|
+
return <DiffSplitContentLine side={side} index={line.index} lineNumber={line.lineNumber} diffFile={diffFile} />;
|
|
26
|
+
case DiffFileLineType.widget:
|
|
27
|
+
return <DiffSplitWidgetLine side={side} index={line.index} lineNumber={line.lineNumber} diffFile={diffFile} />;
|
|
28
|
+
case DiffFileLineType.extend:
|
|
29
|
+
return <DiffSplitExtendLine side={side} index={line.index} lineNumber={line.lineNumber} diffFile={diffFile} />;
|
|
30
|
+
default:
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { DiffFileLineType } from "@git-diff-view/core";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
import { DiffSplitContentLine } from "./DiffSplitContentLineWrap_v2";
|
|
5
|
+
import { DiffSplitExtendLine } from "./DiffSplitExtendLineWrap_v2";
|
|
6
|
+
import { DiffSplitHunkLine } from "./DiffSplitHunkLineWrap_v2";
|
|
7
|
+
import { DiffSplitWidgetLine } from "./DiffSplitWidgetLineWrap_v2";
|
|
8
|
+
|
|
9
|
+
import type { DiffFile, DiffSplitLineItem } from "@git-diff-view/core";
|
|
10
|
+
|
|
11
|
+
export const DiffSplitViewLine = ({ line, diffFile }: { line: DiffSplitLineItem; diffFile: DiffFile }) => {
|
|
12
|
+
switch (line.type) {
|
|
13
|
+
case DiffFileLineType.hunk:
|
|
14
|
+
return <DiffSplitHunkLine index={line.index} lineNumber={line.lineNumber} diffFile={diffFile} />;
|
|
15
|
+
case DiffFileLineType.content:
|
|
16
|
+
return <DiffSplitContentLine index={line.index} lineNumber={line.lineNumber} diffFile={diffFile} />;
|
|
17
|
+
case DiffFileLineType.widget:
|
|
18
|
+
return <DiffSplitWidgetLine index={line.index} lineNumber={line.lineNumber} diffFile={diffFile} />;
|
|
19
|
+
case DiffFileLineType.extend:
|
|
20
|
+
return <DiffSplitExtendLine index={line.index} lineNumber={line.lineNumber} diffFile={diffFile} />;
|
|
21
|
+
default:
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
|
2
|
+
import { DiffFileLineType, getSplitLines, type DiffFile } from "@git-diff-view/core";
|
|
3
|
+
import {
|
|
4
|
+
removeAllSelection,
|
|
5
|
+
syncScroll,
|
|
6
|
+
diffFontSizeName,
|
|
7
|
+
diffAsideWidthName,
|
|
8
|
+
borderColorName,
|
|
9
|
+
} from "@git-diff-view/utils";
|
|
10
|
+
import { memo, useEffect, useRef } from "react";
|
|
11
|
+
import * as React from "react";
|
|
12
|
+
import { useSyncExternalStore } from "use-sync-external-store/shim/index.js";
|
|
13
|
+
|
|
14
|
+
import { useTextWidth } from "../../hooks/useTextWidth";
|
|
15
|
+
import { SplitSide } from "../DiffView";
|
|
16
|
+
import { useDiffViewContext } from "../DiffViewContext";
|
|
17
|
+
|
|
18
|
+
import { DiffSplitViewLine } from "./DiffSplitViewLineNormal_v2";
|
|
19
|
+
|
|
20
|
+
import type { MouseEventHandler } from "react";
|
|
21
|
+
|
|
22
|
+
export const DiffSplitViewTable = ({
|
|
23
|
+
side,
|
|
24
|
+
diffFile,
|
|
25
|
+
onMouseDown,
|
|
26
|
+
}: {
|
|
27
|
+
side: SplitSide;
|
|
28
|
+
diffFile: DiffFile;
|
|
29
|
+
onMouseDown?: MouseEventHandler<HTMLDivElement>;
|
|
30
|
+
}) => {
|
|
31
|
+
const className = side === SplitSide.new ? "new-diff-table" : "old-diff-table";
|
|
32
|
+
|
|
33
|
+
const lines = getSplitLines(diffFile);
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<div className={className + " w-max min-w-full"} data-mode={SplitSide[side]}>
|
|
37
|
+
<div className="diff-table-body leading-[1.6]" onMouseDownCapture={onMouseDown}>
|
|
38
|
+
{lines.map((line) => (
|
|
39
|
+
<DiffSplitViewLine
|
|
40
|
+
key={line.index + "-" + DiffFileLineType[line.type]}
|
|
41
|
+
side={side}
|
|
42
|
+
line={line}
|
|
43
|
+
diffFile={diffFile}
|
|
44
|
+
/>
|
|
45
|
+
))}
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const DiffSplitViewNormal = memo(({ diffFile }: { diffFile: DiffFile }) => {
|
|
52
|
+
const ref1 = useRef<HTMLDivElement>(null);
|
|
53
|
+
|
|
54
|
+
const ref2 = useRef<HTMLDivElement>(null);
|
|
55
|
+
|
|
56
|
+
const ref = useRef<HTMLStyleElement>();
|
|
57
|
+
|
|
58
|
+
const splitLineLength = Math.max(diffFile.splitLineLength, diffFile.fileLineLength);
|
|
59
|
+
|
|
60
|
+
const { useDiffContext } = useDiffViewContext();
|
|
61
|
+
|
|
62
|
+
const fontSize = useDiffContext.useShallowStableSelector((s) => s.fontSize);
|
|
63
|
+
|
|
64
|
+
useSyncExternalStore(diffFile.subscribe, diffFile.getUpdateCount, diffFile.getUpdateCount);
|
|
65
|
+
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
const left = ref1.current;
|
|
68
|
+
const right = ref2.current;
|
|
69
|
+
if (!left || !right) return;
|
|
70
|
+
return syncScroll(left, right);
|
|
71
|
+
}, []);
|
|
72
|
+
|
|
73
|
+
const font = React.useMemo(
|
|
74
|
+
() => ({ fontSize: fontSize + "px", fontFamily: "Menlo, Consolas, monospace" }),
|
|
75
|
+
[fontSize]
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const _width = useTextWidth({
|
|
79
|
+
text: splitLineLength.toString(),
|
|
80
|
+
font,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const width = Math.max(40, _width + 25);
|
|
84
|
+
|
|
85
|
+
const setStyle = (side: SplitSide) => {
|
|
86
|
+
if (!ref.current) return;
|
|
87
|
+
if (!side) {
|
|
88
|
+
ref.current.textContent = "";
|
|
89
|
+
} else {
|
|
90
|
+
const id = `diff-root${diffFile.getId()}`;
|
|
91
|
+
ref.current.textContent = `#${id} [data-state="extend"] {user-select: none} \n#${id} [data-state="hunk"] {user-select: none} \n#${id} [data-state="widget"] {user-select: none}`;
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const onMouseDown: MouseEventHandler<HTMLTableSectionElement> = (e) => {
|
|
96
|
+
let ele = e.target;
|
|
97
|
+
|
|
98
|
+
// need remove all the selection
|
|
99
|
+
if (ele && ele instanceof HTMLElement && ele.nodeName === "BUTTON") {
|
|
100
|
+
removeAllSelection();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
while (ele && ele instanceof HTMLElement) {
|
|
105
|
+
const state = ele.getAttribute("data-state");
|
|
106
|
+
const side = ele.getAttribute("data-side");
|
|
107
|
+
if (side) {
|
|
108
|
+
setStyle(SplitSide[side]);
|
|
109
|
+
removeAllSelection();
|
|
110
|
+
}
|
|
111
|
+
if (state) {
|
|
112
|
+
if (state === "extend" || state === "hunk" || state === "widget") {
|
|
113
|
+
setStyle(undefined);
|
|
114
|
+
removeAllSelection();
|
|
115
|
+
return;
|
|
116
|
+
} else {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
ele = ele.parentElement;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<div className="split-diff-view split-diff-view-normal flex w-full basis-[50%]">
|
|
127
|
+
<style data-select-style ref={ref} />
|
|
128
|
+
<div
|
|
129
|
+
className="old-diff-table-wrapper diff-table-scroll-container w-full overflow-x-auto overflow-y-hidden"
|
|
130
|
+
ref={ref1}
|
|
131
|
+
style={{
|
|
132
|
+
// @ts-ignore
|
|
133
|
+
[diffAsideWidthName]: `${Math.round(width)}px`,
|
|
134
|
+
overscrollBehaviorX: "none",
|
|
135
|
+
fontFamily: "Menlo, Consolas, monospace",
|
|
136
|
+
fontSize: `var(${diffFontSizeName})`,
|
|
137
|
+
}}
|
|
138
|
+
>
|
|
139
|
+
<DiffSplitViewTable side={SplitSide.old} diffFile={diffFile} onMouseDown={onMouseDown} />
|
|
140
|
+
</div>
|
|
141
|
+
<div className="diff-split-line w-[1.5px]" style={{ backgroundColor: `var(${borderColorName})` }} />
|
|
142
|
+
<div
|
|
143
|
+
className="new-diff-table-wrapper diff-table-scroll-container w-full overflow-x-auto overflow-y-hidden"
|
|
144
|
+
ref={ref2}
|
|
145
|
+
style={{
|
|
146
|
+
// @ts-ignore
|
|
147
|
+
[diffAsideWidthName]: `${Math.round(width)}px`,
|
|
148
|
+
overscrollBehaviorX: "none",
|
|
149
|
+
fontFamily: "Menlo, Consolas, monospace",
|
|
150
|
+
fontSize: `var(${diffFontSizeName})`,
|
|
151
|
+
}}
|
|
152
|
+
>
|
|
153
|
+
<DiffSplitViewTable side={SplitSide.new} diffFile={diffFile} onMouseDown={onMouseDown} />
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
DiffSplitViewNormal.displayName = "DiffSplitViewNormal";
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
|
2
|
+
import { type DiffFile, getSplitLines } from "@git-diff-view/core";
|
|
3
|
+
import { removeAllSelection, diffFontSizeName, diffAsideWidthName } from "@git-diff-view/utils";
|
|
4
|
+
import { memo, useMemo, useRef } from "react";
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
// SEE https://github.com/facebook/react/pull/25231
|
|
7
|
+
import { useSyncExternalStore } from "use-sync-external-store/shim/index.js";
|
|
8
|
+
|
|
9
|
+
import { useTextWidth } from "../../hooks/useTextWidth";
|
|
10
|
+
import { SplitSide } from "../DiffView";
|
|
11
|
+
import { useDiffViewContext } from "../DiffViewContext";
|
|
12
|
+
|
|
13
|
+
import { DiffSplitViewLine } from "./DiffSplitViewLineWrap_v2";
|
|
14
|
+
|
|
15
|
+
import type { MouseEventHandler } from "react";
|
|
16
|
+
|
|
17
|
+
export const DiffSplitViewWrap = memo(({ diffFile }: { diffFile: DiffFile }) => {
|
|
18
|
+
const splitLineLength = Math.max(diffFile.splitLineLength, diffFile.fileLineLength);
|
|
19
|
+
|
|
20
|
+
const { useDiffContext } = useDiffViewContext();
|
|
21
|
+
|
|
22
|
+
const ref = useRef<HTMLStyleElement>(null);
|
|
23
|
+
|
|
24
|
+
const fontSize = useDiffContext.useShallowStableSelector((s) => s.fontSize);
|
|
25
|
+
|
|
26
|
+
useSyncExternalStore(diffFile.subscribe, diffFile.getUpdateCount, diffFile.getUpdateCount);
|
|
27
|
+
|
|
28
|
+
const font = useMemo(() => ({ fontSize: fontSize + "px", fontFamily: "Menlo, Consolas, monospace" }), [fontSize]);
|
|
29
|
+
|
|
30
|
+
const _width = useTextWidth({
|
|
31
|
+
text: splitLineLength.toString(),
|
|
32
|
+
font,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const width = Math.max(40, _width + 25);
|
|
36
|
+
|
|
37
|
+
const lines = getSplitLines(diffFile);
|
|
38
|
+
|
|
39
|
+
const setStyle = (side: SplitSide) => {
|
|
40
|
+
if (!ref.current) return;
|
|
41
|
+
if (!side) {
|
|
42
|
+
ref.current.textContent = "";
|
|
43
|
+
} else {
|
|
44
|
+
const id = `diff-root${diffFile.getId()}`;
|
|
45
|
+
const targetSide = side === SplitSide.old ? SplitSide.new : SplitSide.old;
|
|
46
|
+
ref.current.textContent = `#${id} [data-side="${SplitSide[targetSide]}"] {user-select: none} \n#${id} [data-state="extend"] {user-select: none} \n#${id} [data-state="hunk"] {user-select: none} \n#${id} [data-state="widget"] {user-select: none}`;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const onMouseDown: MouseEventHandler<HTMLTableSectionElement> = (e) => {
|
|
51
|
+
let ele = e.target;
|
|
52
|
+
|
|
53
|
+
// need remove all the selection
|
|
54
|
+
if (ele && ele instanceof HTMLElement && ele.nodeName === "BUTTON") {
|
|
55
|
+
removeAllSelection();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
while (ele && ele instanceof HTMLElement) {
|
|
60
|
+
const state = ele.getAttribute("data-state");
|
|
61
|
+
const side = ele.getAttribute("data-side");
|
|
62
|
+
if (side) {
|
|
63
|
+
setStyle(SplitSide[side]);
|
|
64
|
+
removeAllSelection();
|
|
65
|
+
}
|
|
66
|
+
if (state) {
|
|
67
|
+
if (state === "extend" || state === "hunk" || state === "widget") {
|
|
68
|
+
setStyle(undefined);
|
|
69
|
+
removeAllSelection();
|
|
70
|
+
return;
|
|
71
|
+
} else {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
ele = ele.parentElement;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<div className="split-diff-view split-diff-view-warp w-full">
|
|
82
|
+
<div
|
|
83
|
+
className="diff-table-wrapper w-full"
|
|
84
|
+
style={{
|
|
85
|
+
// @ts-ignore
|
|
86
|
+
[diffAsideWidthName]: `${Math.round(width)}px`,
|
|
87
|
+
fontFamily: "Menlo, Consolas, monospace",
|
|
88
|
+
fontSize: `var(${diffFontSizeName})`,
|
|
89
|
+
}}
|
|
90
|
+
>
|
|
91
|
+
<style data-select-style ref={ref} />
|
|
92
|
+
<div className="diff-table w-full table-fixed border-collapse">
|
|
93
|
+
<div className="diff-table-body leading-[1.6]" onMouseDownCapture={onMouseDown}>
|
|
94
|
+
{lines.map((line, index) => (
|
|
95
|
+
<DiffSplitViewLine key={line.index + index} line={line} diffFile={diffFile} />
|
|
96
|
+
))}
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
DiffSplitViewWrap.displayName = "DiffSplitViewWrap";
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { memo, useEffect, useMemo, useRef } from "react";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
import { useDiffViewContext } from "../DiffViewContext";
|
|
5
|
+
import { DiffWidgetContext } from "../DiffWidgetContext";
|
|
6
|
+
import { createDiffWidgetStore } from "../tools";
|
|
7
|
+
|
|
8
|
+
import { DiffSplitViewNormal } from "./DiffSplitViewNormal_v2";
|
|
9
|
+
import { DiffSplitViewWrap } from "./DiffSplitViewWrap_v2";
|
|
10
|
+
|
|
11
|
+
import type { DiffFile } from "@git-diff-view/core";
|
|
12
|
+
|
|
13
|
+
export const DiffSplitView = memo(({ diffFile }: { diffFile: DiffFile }) => {
|
|
14
|
+
const { useDiffContext } = useDiffViewContext();
|
|
15
|
+
|
|
16
|
+
const useDiffContextRef = useRef(useDiffContext);
|
|
17
|
+
|
|
18
|
+
useDiffContextRef.current = useDiffContext;
|
|
19
|
+
|
|
20
|
+
const { enableWrap, onCreateUseWidgetHook } = useDiffContext.useShallowStableSelector((s) => ({
|
|
21
|
+
enableWrap: s.enableWrap,
|
|
22
|
+
onCreateUseWidgetHook: s.onCreateUseWidgetHook,
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
// performance optimization
|
|
26
|
+
const useWidget = useMemo(() => createDiffWidgetStore(useDiffContextRef), []);
|
|
27
|
+
|
|
28
|
+
const contextValue = useMemo(() => ({ useWidget }), [useWidget]);
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
const { setWidget } = useWidget.getReadonlyState();
|
|
32
|
+
|
|
33
|
+
setWidget({});
|
|
34
|
+
}, [diffFile, useWidget]);
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
onCreateUseWidgetHook?.(useWidget);
|
|
38
|
+
}, [useWidget, onCreateUseWidgetHook]);
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<DiffWidgetContext.Provider value={contextValue}>
|
|
42
|
+
{enableWrap ? <DiffSplitViewWrap diffFile={diffFile} /> : <DiffSplitViewNormal diffFile={diffFile} />}
|
|
43
|
+
</DiffWidgetContext.Provider>
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
DiffSplitView.displayName = "DiffSplitView";
|