@git-diff-view/react 0.1.2 → 0.1.4

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.
Files changed (59) hide show
  1. package/dist/cjs/index.development.js +497 -503
  2. package/dist/cjs/index.development.js.map +1 -1
  3. package/dist/cjs/index.production.js +497 -503
  4. package/dist/cjs/index.production.js.map +1 -1
  5. package/dist/css/diff-view-pure.css +111 -0
  6. package/dist/css/diff-view.css +111 -0
  7. package/dist/esm/index.mjs +499 -506
  8. package/dist/esm/index.mjs.map +1 -1
  9. package/index.d.ts +239 -7
  10. package/package.json +2 -2
  11. package/src/_com.css +99 -0
  12. package/src/components/DiffAddWidget.tsx +4 -3
  13. package/src/components/DiffContent.tsx +6 -6
  14. package/src/components/DiffExpand.tsx +0 -2
  15. package/src/components/DiffNoNewLine.tsx +0 -2
  16. package/src/components/DiffSplitContentLineNormal.tsx +3 -4
  17. package/src/components/DiffSplitContentLineWrap.tsx +9 -10
  18. package/src/components/DiffSplitExtendLineNormal.tsx +3 -3
  19. package/src/components/DiffSplitExtendLineWrap.tsx +4 -4
  20. package/src/components/DiffSplitHunkLineNormal.tsx +0 -1
  21. package/src/components/DiffSplitHunkLineWrap.tsx +0 -1
  22. package/src/components/DiffSplitView.tsx +0 -1
  23. package/src/components/DiffSplitViewNormal.tsx +13 -12
  24. package/src/components/DiffSplitViewWrap.tsx +10 -10
  25. package/src/components/DiffSplitWidgetLineNormal.tsx +2 -2
  26. package/src/components/DiffSplitWidgetLineWrap.tsx +12 -2
  27. package/src/components/DiffUnifiedContentLine.tsx +1 -2
  28. package/src/components/DiffUnifiedExtendLine.tsx +4 -5
  29. package/src/components/DiffUnifiedHunkLine.tsx +0 -1
  30. package/src/components/DiffUnifiedView.tsx +9 -9
  31. package/src/components/DiffUnifiedWidgetLine.tsx +2 -2
  32. package/src/components/DiffView.tsx +26 -20
  33. package/src/components/DiffViewContext.ts +2 -0
  34. package/src/components/DiffViewWithMultiSelect.tsx +321 -0
  35. package/src/components/DiffWidgetContext.ts +4 -2
  36. package/src/components/tools.ts +6 -5
  37. package/src/components/v2/DiffSplitContentLineNormal_v2.tsx +4 -5
  38. package/src/components/v2/DiffSplitContentLineWrap_v2.tsx +11 -12
  39. package/src/components/v2/DiffSplitExtendLineNormal_v2.tsx +3 -3
  40. package/src/components/v2/DiffSplitExtendLineWrap_v2.tsx +4 -4
  41. package/src/components/v2/DiffSplitHunkLineNormal_v2.tsx +0 -1
  42. package/src/components/v2/DiffSplitHunkLineWrap_v2.tsx +0 -1
  43. package/src/components/v2/DiffSplitViewLineNormal_v2.tsx +0 -1
  44. package/src/components/v2/DiffSplitViewLineWrap_v2.tsx +0 -1
  45. package/src/components/v2/DiffSplitViewNormal_v2.tsx +9 -8
  46. package/src/components/v2/DiffSplitViewWrap_v2.tsx +5 -5
  47. package/src/components/v2/DiffSplitView_v2.tsx +0 -1
  48. package/src/components/v2/DiffSplitWidgetLineNormal_v2.tsx +2 -3
  49. package/src/components/v2/DiffSplitWidgetLineWrap_v2.tsx +4 -5
  50. package/src/hooks/useCallbackRef.ts +5 -10
  51. package/src/hooks/useDomWidth.ts +5 -5
  52. package/src/hooks/useIsMounted.ts +1 -0
  53. package/src/hooks/useSyncHeight.ts +3 -3
  54. package/src/hooks/useTextWidth.ts +1 -1
  55. package/src/hooks/useUnmount.ts +1 -0
  56. package/src/hooks/useUpdateEffect.ts +15 -0
  57. package/src/index.ts +1 -2
  58. package/styles/diff-view-pure.css +111 -0
  59. package/styles/diff-view.css +111 -0
@@ -0,0 +1,321 @@
1
+ import { useCallback, useEffect, useMemo, useRef, useImperativeHandle, forwardRef } from "react";
2
+
3
+ import { multiSelectClassNames, createDiffMultiSelectManager } from "..";
4
+ import { useCallbackRef } from "../hooks/useCallbackRef";
5
+ import { useUpdateEffect } from "../hooks/useUpdateEffect";
6
+
7
+ import { DiffModeEnum, DiffView, SplitSide } from "./DiffView";
8
+
9
+ import type {
10
+ MultiSelectResult,
11
+ LineRange,
12
+ MultiSelectState,
13
+ DiffFile,
14
+ DiffMultiSelectManager,
15
+ MultiSelectOptions,
16
+ extendDataToPreselectedLines,
17
+ } from "..";
18
+ import type { DiffViewProps } from "./DiffView";
19
+ import type { ForwardedRef, ReactNode } from "react";
20
+
21
+ export interface DiffViewWithMultiSelectProps<T = unknown> extends Omit<
22
+ DiffViewProps<T>,
23
+ "renderWidgetLine" | "onAddWidgetClick"
24
+ > {
25
+ /**
26
+ * Enable multi-select feature
27
+ * @default true
28
+ */
29
+ enableMultiSelect?: boolean;
30
+
31
+ /**
32
+ * Callback when multi-line selection is complete
33
+ * Use this to open a comment dialog or handle the selection
34
+ */
35
+ onMultiSelectComplete?: (result: MultiSelectResult) => void;
36
+
37
+ /**
38
+ * Callback when selection changes (during drag)
39
+ */
40
+ onMultiSelectChange?: (range: LineRange | null, state: MultiSelectState) => void;
41
+
42
+ /**
43
+ * Custom function to scope selection to one hunk
44
+ * Return the scoped range or null to cancel selection
45
+ */
46
+ scopeMultiSelectToHunk?: (range: LineRange) => LineRange | null;
47
+
48
+ onAddWidgetClick?: (props: { lineNumber: number; fromLineNumber?: number; side: SplitSide }) => void;
49
+
50
+ renderWidgetLine?: (props: {
51
+ lineNumber: number;
52
+ fromLineNumber: number;
53
+ side: SplitSide;
54
+ diffFile: DiffFile;
55
+ onClose: () => void;
56
+ }) => ReactNode;
57
+ }
58
+
59
+ export interface DiffViewWithMultiSelectRef {
60
+ getDiffFileInstance: () => DiffFile | null;
61
+ getSelectionResult: () => MultiSelectResult | null;
62
+ getSelectionState: () => MultiSelectState;
63
+ clearSelection: () => void;
64
+ setPreselectedLines: (lines: { old: number[]; new: number[] }) => void;
65
+ }
66
+
67
+ type MultiResult = ReturnType<typeof extendDataToPreselectedLines>;
68
+
69
+ /* eslint-disable @typescript-eslint/no-unnecessary-type-constraint */
70
+ const InternalDiffViewWithMultiSelect = <T extends unknown>(
71
+ props: DiffViewWithMultiSelectProps<T>,
72
+ ref: ForwardedRef<DiffViewWithMultiSelectRef>
73
+ ) => {
74
+ const {
75
+ enableMultiSelect = true,
76
+ extendData,
77
+ onMultiSelectComplete,
78
+ onMultiSelectChange,
79
+ scopeMultiSelectToHunk,
80
+ renderWidgetLine,
81
+ onAddWidgetClick,
82
+ diffViewMode = DiffModeEnum.SplitGitHub,
83
+ ...restProps
84
+ } = props;
85
+
86
+ const memoSelectChange = useCallbackRef(onMultiSelectChange);
87
+
88
+ const memoSelectComplete = useCallbackRef(onMultiSelectComplete);
89
+
90
+ const memoScopeSelectToHunk = useCallbackRef(scopeMultiSelectToHunk);
91
+
92
+ const containerRef = useRef<HTMLDivElement>(null);
93
+ const diffViewRef = useRef<{ getDiffFileInstance: () => DiffFile | null }>(null);
94
+ const managerRef = useRef<DiffMultiSelectManager | null>(null);
95
+
96
+ const multiResultRef = useRef<MultiResult>(undefined);
97
+
98
+ const isUnifiedMode = !(diffViewMode & DiffModeEnum.Split);
99
+
100
+ const updateMultiResult = useCallback((result?: MultiResult) => {
101
+ multiResultRef.current = result;
102
+ managerRef.current?.setPreselectedLines(result || { old: [], new: [] });
103
+ }, []);
104
+
105
+ useUpdateEffect(() => {
106
+ updateMultiResult(undefined);
107
+ }, [props.diffViewWrap, diffViewMode]);
108
+
109
+ const getDiffFile = useCallback(() => {
110
+ return diffViewRef.current?.getDiffFileInstance() ?? null;
111
+ }, []);
112
+
113
+ useEffect(() => {
114
+ const container = containerRef.current;
115
+ const diffFile = getDiffFile();
116
+
117
+ if (!container || !diffFile || !enableMultiSelect) {
118
+ managerRef.current?.destroy();
119
+ managerRef.current = null;
120
+ return;
121
+ }
122
+
123
+ const managerOptions: MultiSelectOptions = {
124
+ enabled: enableMultiSelect,
125
+ isUnifiedMode,
126
+ selectedClassName: multiSelectClassNames.selected,
127
+ onSelectionChange: (range, state) => {
128
+ if (state.isSelecting) {
129
+ containerRef.current?.classList.add(multiSelectClassNames.selecting);
130
+ } else {
131
+ containerRef.current?.classList.remove(multiSelectClassNames.selecting);
132
+ }
133
+ if (state.isSelecting && multiResultRef.current) {
134
+ updateMultiResult(undefined);
135
+ }
136
+ memoSelectChange?.(range, state);
137
+ },
138
+ onSelectionComplete: (result) => {
139
+ containerRef.current?.classList.remove(multiSelectClassNames.selecting);
140
+ if (result && result.lines.length > 0) {
141
+ memoSelectComplete?.(result);
142
+ const finalResult = {
143
+ [result.range.side as "old" | "new"]: [result.range.startLineNumber, result.range.endLineNumber],
144
+ } as MultiResult;
145
+ updateMultiResult(finalResult);
146
+ } else {
147
+ updateMultiResult(undefined);
148
+ }
149
+ },
150
+ scopeToHunk: memoScopeSelectToHunk,
151
+ };
152
+
153
+ if (managerRef.current) {
154
+ managerRef.current.updateContainer(container);
155
+ managerRef.current.updateDiffFile(diffFile);
156
+ managerRef.current.updateOptions(managerOptions);
157
+ } else {
158
+ managerRef.current = createDiffMultiSelectManager(container, diffFile, managerOptions);
159
+ }
160
+
161
+ return () => {
162
+ managerRef.current?.destroy();
163
+ managerRef.current = null;
164
+ };
165
+ }, [
166
+ enableMultiSelect,
167
+ isUnifiedMode,
168
+ memoScopeSelectToHunk,
169
+ memoSelectChange,
170
+ memoSelectComplete,
171
+ getDiffFile,
172
+ updateMultiResult,
173
+ ]);
174
+
175
+ const convertedExtendData = useMemo(() => {
176
+ if (!extendData) return undefined;
177
+
178
+ const result: { oldFile?: Record<string, { data: T }>; newFile?: Record<string, { data: T }> } = {};
179
+
180
+ if (extendData.oldFile) {
181
+ result.oldFile = {};
182
+ for (const [key, value] of Object.entries(extendData.oldFile)) {
183
+ result.oldFile[key] = { data: value.data };
184
+ }
185
+ }
186
+
187
+ if (extendData.newFile) {
188
+ result.newFile = {};
189
+ for (const [key, value] of Object.entries(extendData.newFile)) {
190
+ result.newFile[key] = { data: value.data };
191
+ }
192
+ }
193
+
194
+ return result;
195
+ }, [extendData]);
196
+
197
+ const internalRenderWidgetLine = useCallback(
198
+ ({
199
+ lineNumber,
200
+ side,
201
+ diffFile,
202
+ onClose,
203
+ }: {
204
+ lineNumber: number;
205
+ side: SplitSide;
206
+ diffFile: DiffFile;
207
+ onClose: () => void;
208
+ }) => {
209
+ if (!renderWidgetLine) return null;
210
+
211
+ const sideKey = side === SplitSide.old ? "old" : "new";
212
+ const multiResultItem = multiResultRef.current?.[sideKey] as number[];
213
+ const fromLineNumber = multiResultItem ? Math.min(...multiResultItem) : lineNumber;
214
+ const toLineNumber = multiResultItem ? Math.max(...multiResultItem) : lineNumber;
215
+
216
+ return renderWidgetLine({
217
+ lineNumber: toLineNumber,
218
+ fromLineNumber,
219
+ side,
220
+ diffFile,
221
+ onClose,
222
+ });
223
+ },
224
+ [renderWidgetLine]
225
+ );
226
+
227
+ const getSelectionResult = useCallback(() => {
228
+ return managerRef.current?.getSelectionResult() ?? null;
229
+ }, []);
230
+
231
+ const getSelectionState = useCallback(() => {
232
+ return (
233
+ managerRef.current?.getState() ?? {
234
+ isSelecting: false,
235
+ startInfo: null,
236
+ currentRange: null,
237
+ }
238
+ );
239
+ }, []);
240
+
241
+ const clearSelection = useCallback(() => {
242
+ managerRef.current?.clearSelection();
243
+ }, []);
244
+
245
+ const setPreselectedLines = updateMultiResult;
246
+
247
+ useImperativeHandle(
248
+ ref,
249
+ () => ({
250
+ getDiffFileInstance: getDiffFile,
251
+ getSelectionResult,
252
+ getSelectionState,
253
+ clearSelection,
254
+ setPreselectedLines,
255
+ }),
256
+ [getDiffFile, getSelectionResult, getSelectionState, clearSelection, setPreselectedLines]
257
+ );
258
+
259
+ return (
260
+ <div ref={containerRef} className="diff-multiselect-wrapper">
261
+ <DiffView
262
+ ref={diffViewRef}
263
+ {...restProps}
264
+ diffViewMode={diffViewMode}
265
+ extendData={convertedExtendData}
266
+ onAddWidgetClick={(lineNum: number, side: SplitSide) => {
267
+ managerRef.current?.clearSelection();
268
+ const multiResult = multiResultRef.current;
269
+ if (multiResult) {
270
+ const currentSide = SplitSide[side] as unknown as "new" | "old";
271
+ const currentMultiResult = multiResult[currentSide] as number[];
272
+ const otherSide = currentSide === "new" ? "old" : "new";
273
+ const otherMultiResult = multiResult[otherSide] as number[];
274
+ if (currentMultiResult?.length) {
275
+ const max = Math.max(...currentMultiResult);
276
+ if (max === lineNum) {
277
+ const finalResult = { [currentSide]: currentMultiResult };
278
+ updateMultiResult(finalResult as MultiResult);
279
+ onAddWidgetClick?.({ lineNumber: max, fromLineNumber: Math.min(...currentMultiResult), side });
280
+ return;
281
+ }
282
+ }
283
+ if (isUnifiedMode && otherMultiResult?.length) {
284
+ const max = Math.max(...otherMultiResult);
285
+ const diffFile = getDiffFile();
286
+ const index = diffFile.getUnifiedLineIndexByLineNumber(lineNum, side);
287
+ const unifiedItem = diffFile.getUnifiedLine(index);
288
+ const otherSideLineNum = side === SplitSide.old ? unifiedItem.newLineNumber : unifiedItem.oldLineNumber;
289
+ if (max === otherSideLineNum) {
290
+ const finalResult = { [otherSide]: otherMultiResult };
291
+ updateMultiResult(finalResult as MultiResult);
292
+ onAddWidgetClick?.({
293
+ lineNumber: max,
294
+ fromLineNumber: Math.min(...otherMultiResult),
295
+ side: otherSide === "old" ? SplitSide.old : SplitSide.new,
296
+ });
297
+ return;
298
+ }
299
+ }
300
+ updateMultiResult(undefined);
301
+ onAddWidgetClick?.({ lineNumber: lineNum, fromLineNumber: lineNum, side });
302
+ } else {
303
+ updateMultiResult(undefined);
304
+ onAddWidgetClick?.({ lineNumber: lineNum, fromLineNumber: lineNum, side });
305
+ }
306
+ }}
307
+ renderWidgetLine={renderWidgetLine ? internalRenderWidgetLine : undefined}
308
+ />
309
+ </div>
310
+ );
311
+ };
312
+
313
+ // type helper function
314
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
315
+ function ReactDiffView<T>(
316
+ _props: DiffViewWithMultiSelectProps<T> & { ref?: ForwardedRef<DiffViewWithMultiSelectRef> }
317
+ ) {
318
+ return <></>;
319
+ }
320
+
321
+ export const DiffViewWithMultiSelect = forwardRef(InternalDiffViewWithMultiSelect) as typeof ReactDiffView;
@@ -5,11 +5,13 @@ import type { Ref, UseSelectorWithStore } from "reactivity-store";
5
5
 
6
6
  export const DiffWidgetContext = createContext<{
7
7
  useWidget: UseSelectorWithStore<{
8
- widgetSide: Ref<SplitSide>;
9
- widgetLineNumber: Ref<number>;
8
+ widgetSide: Ref<SplitSide | undefined>;
9
+ widgetLineNumber: Ref<number | undefined>;
10
10
 
11
11
  setWidget: (props: { side?: SplitSide; lineNumber?: number }) => void;
12
12
  }>;
13
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
14
+ // @ts-ignore
13
15
  }>(null);
14
16
 
15
17
  DiffWidgetContext.displayName = "DiffWidgetContext";
@@ -42,7 +42,8 @@ export const createDiffConfigStore = (props: DiffViewProps<any> & { isMounted: b
42
42
  newFile: { ...props.extendData?.newFile },
43
43
  });
44
44
 
45
- const setExtendData = (_extendData: DiffViewProps<any>["extendData"]) => {
45
+ const setExtendData = (__extendData: DiffViewProps<any>["extendData"]) => {
46
+ const _extendData = __extendData || {};
46
47
  const existOldKeys = Object.keys(extendData.value.oldFile || {});
47
48
  const inComingOldKeys = Object.keys(_extendData.oldFile || {});
48
49
  for (const key of existOldKeys) {
@@ -51,7 +52,7 @@ export const createDiffConfigStore = (props: DiffViewProps<any> & { isMounted: b
51
52
  }
52
53
  }
53
54
  for (const key of inComingOldKeys) {
54
- extendData.value.oldFile[key] = _extendData.oldFile[key];
55
+ extendData.value.oldFile[key] = _extendData.oldFile![key];
55
56
  }
56
57
  const existNewKeys = Object.keys(extendData.value.newFile || {});
57
58
  const inComingNewKeys = Object.keys(_extendData.newFile || {});
@@ -61,7 +62,7 @@ export const createDiffConfigStore = (props: DiffViewProps<any> & { isMounted: b
61
62
  }
62
63
  }
63
64
  for (const key of inComingNewKeys) {
64
- extendData.value.newFile[key] = _extendData.newFile[key];
65
+ extendData.value.newFile[key] = _extendData.newFile![key];
65
66
  }
66
67
  };
67
68
 
@@ -119,9 +120,9 @@ export const createDiffConfigStore = (props: DiffViewProps<any> & { isMounted: b
119
120
 
120
121
  export const createDiffWidgetStore = (useDiffContextRef: RefObject<ReturnType<typeof createDiffConfigStore>>) => {
121
122
  return createStore(() => {
122
- const widgetSide = ref<SplitSide>(undefined);
123
+ const widgetSide = ref<SplitSide | undefined>(undefined);
123
124
 
124
- const widgetLineNumber = ref<number>(undefined);
125
+ const widgetLineNumber = ref<number | undefined>(undefined);
125
126
 
126
127
  const setWidget = ({ side, lineNumber }: { side?: SplitSide; lineNumber?: number }) => {
127
128
  const { renderWidgetLine } = useDiffContextRef.current?.getReadonlyState?.() || {};
@@ -7,7 +7,6 @@ import {
7
7
  diffAsideWidthName,
8
8
  expandLineNumberColorName,
9
9
  } from "@git-diff-view/utils";
10
- import * as React from "react";
11
10
 
12
11
  import { DiffSplitAddWidget } from "../DiffAddWidget";
13
12
  import { DiffContent } from "../DiffContent";
@@ -62,9 +61,9 @@ const InternalDiffSplitLine = ({
62
61
 
63
62
  const lineNumberBG = getLineNumberBG(isAdded, isDelete, hasDiff);
64
63
 
65
- const plainLine = getCurrentPlainLine(currentLine.lineNumber);
64
+ const plainLine = getCurrentPlainLine(currentLine.lineNumber ?? -1);
66
65
 
67
- const syntaxLine = getCurrentSyntaxLine(currentLine.lineNumber);
66
+ const syntaxLine = getCurrentSyntaxLine(currentLine.lineNumber ?? -1);
68
67
 
69
68
  return (
70
69
  <div
@@ -88,7 +87,7 @@ const InternalDiffSplitLine = ({
88
87
  {hasDiff && enableAddWidget && (
89
88
  <DiffSplitAddWidget
90
89
  index={index}
91
- lineNumber={currentLine.lineNumber}
90
+ lineNumber={currentLine.lineNumber ?? -1}
92
91
  side={side}
93
92
  diffFile={diffFile}
94
93
  onWidgetClick={(...props) => onAddWidgetClick.current?.(...props)}
@@ -115,7 +114,7 @@ const InternalDiffSplitLine = ({
115
114
  diffLine={currentLine.diff}
116
115
  plainLine={plainLine}
117
116
  syntaxLine={syntaxLine}
118
- enableHighlight={enableHighlight}
117
+ enableHighlight={!!enableHighlight}
119
118
  />
120
119
  </div>
121
120
  </>
@@ -8,7 +8,6 @@ import {
8
8
  diffAsideWidthName,
9
9
  expandLineNumberColorName,
10
10
  } from "@git-diff-view/utils";
11
- import * as React from "react";
12
11
 
13
12
  import { DiffSplitAddWidget } from "../DiffAddWidget";
14
13
  import { DiffContent } from "../DiffContent";
@@ -29,13 +28,13 @@ const InternalDiffSplitLine = ({
29
28
 
30
29
  const newLine = diffFile.getSplitRightLine(index);
31
30
 
32
- const oldSyntaxLine = diffFile.getOldSyntaxLine(oldLine?.lineNumber);
31
+ const oldSyntaxLine = diffFile.getOldSyntaxLine(oldLine?.lineNumber ?? -1);
33
32
 
34
- const newSyntaxLine = diffFile.getNewSyntaxLine(newLine?.lineNumber);
33
+ const newSyntaxLine = diffFile.getNewSyntaxLine(newLine?.lineNumber ?? -1);
35
34
 
36
- const oldPlainLine = diffFile.getOldPlainLine(oldLine.lineNumber);
35
+ const oldPlainLine = diffFile.getOldPlainLine(oldLine.lineNumber ?? -1);
37
36
 
38
- const newPlainLine = diffFile.getNewPlainLine(newLine.lineNumber);
37
+ const newPlainLine = diffFile.getNewPlainLine(newLine.lineNumber ?? -1);
39
38
 
40
39
  const hasDiff = !!oldLine?.diff || !!newLine?.diff;
41
40
 
@@ -87,7 +86,7 @@ const InternalDiffSplitLine = ({
87
86
  {hasDiff && enableAddWidget && (
88
87
  <DiffSplitAddWidget
89
88
  index={index}
90
- lineNumber={oldLine.lineNumber}
89
+ lineNumber={oldLine.lineNumber ?? -1}
91
90
  side={SplitSide.old}
92
91
  diffFile={diffFile}
93
92
  onWidgetClick={(...props) => onAddWidgetClick.current?.(...props)}
@@ -111,7 +110,7 @@ const InternalDiffSplitLine = ({
111
110
  {hasDiff && enableAddWidget && (
112
111
  <DiffSplitAddWidget
113
112
  index={index}
114
- lineNumber={oldLine.lineNumber}
113
+ lineNumber={oldLine.lineNumber ?? -1}
115
114
  side={SplitSide.old}
116
115
  diffFile={diffFile}
117
116
  onWidgetClick={(...props) => onAddWidgetClick.current?.(...props)}
@@ -122,11 +121,11 @@ const InternalDiffSplitLine = ({
122
121
  <DiffContent
123
122
  enableWrap={true}
124
123
  diffFile={diffFile}
125
- rawLine={oldLine.value}
124
+ rawLine={oldLine.value || ""}
126
125
  diffLine={oldLine.diff}
127
126
  plainLine={oldPlainLine}
128
127
  syntaxLine={oldSyntaxLine}
129
- enableHighlight={enableHighlight}
128
+ enableHighlight={!!enableHighlight}
130
129
  />
131
130
  </div>
132
131
  </>
@@ -170,7 +169,7 @@ const InternalDiffSplitLine = ({
170
169
  {hasDiff && enableAddWidget && (
171
170
  <DiffSplitAddWidget
172
171
  index={index}
173
- lineNumber={newLine.lineNumber}
172
+ lineNumber={newLine.lineNumber ?? -1}
174
173
  side={SplitSide.new}
175
174
  diffFile={diffFile}
176
175
  onWidgetClick={(...props) => onAddWidgetClick.current?.(...props)}
@@ -194,7 +193,7 @@ const InternalDiffSplitLine = ({
194
193
  {hasDiff && enableAddWidget && (
195
194
  <DiffSplitAddWidget
196
195
  index={index}
197
- lineNumber={newLine.lineNumber}
196
+ lineNumber={newLine.lineNumber ?? -1}
198
197
  side={SplitSide.new}
199
198
  diffFile={diffFile}
200
199
  onWidgetClick={(...props) => onAddWidgetClick.current?.(...props)}
@@ -209,7 +208,7 @@ const InternalDiffSplitLine = ({
209
208
  diffLine={newLine.diff}
210
209
  plainLine={newPlainLine}
211
210
  syntaxLine={newSyntaxLine}
212
- enableHighlight={enableHighlight}
211
+ enableHighlight={!!enableHighlight}
213
212
  />
214
213
  </div>
215
214
  </>
@@ -77,7 +77,7 @@ const InternalDiffSplitExtendLine = ({
77
77
  renderExtendLine?.({
78
78
  diffFile,
79
79
  side,
80
- lineNumber: currentLineNumber,
80
+ lineNumber: currentLineNumber ?? -1,
81
81
  data: currentExtend?.data,
82
82
  onUpdate: diffFile.notifyAll,
83
83
  })}
@@ -115,8 +115,8 @@ export const DiffSplitExtendLine = ({
115
115
  const { oldLineExtend, newLineExtend } = useDiffContext(
116
116
  React.useCallback(
117
117
  (s) => ({
118
- oldLineExtend: s.extendData?.oldFile?.[oldLine?.lineNumber],
119
- newLineExtend: s.extendData?.newFile?.[newLine?.lineNumber],
118
+ oldLineExtend: s.extendData?.oldFile?.[oldLine?.lineNumber ?? -1],
119
+ newLineExtend: s.extendData?.newFile?.[newLine?.lineNumber ?? -1],
120
120
  }),
121
121
  [oldLine?.lineNumber, newLine?.lineNumber]
122
122
  )
@@ -35,7 +35,7 @@ const InternalDiffSplitExtendLine = ({
35
35
  renderExtendLine?.({
36
36
  diffFile,
37
37
  side: SplitSide.old,
38
- lineNumber: oldLine.lineNumber,
38
+ lineNumber: oldLine.lineNumber ?? -1,
39
39
  data: oldLineExtend.data,
40
40
  onUpdate: diffFile.notifyAll,
41
41
  });
@@ -45,7 +45,7 @@ const InternalDiffSplitExtendLine = ({
45
45
  renderExtendLine?.({
46
46
  diffFile,
47
47
  side: SplitSide.new,
48
- lineNumber: newLine.lineNumber,
48
+ lineNumber: newLine.lineNumber ?? -1,
49
49
  data: newLineExtend.data,
50
50
  onUpdate: diffFile.notifyAll,
51
51
  });
@@ -95,8 +95,8 @@ export const DiffSplitExtendLine = ({
95
95
  const { oldLineExtend, newLineExtend } = useDiffContext(
96
96
  React.useCallback(
97
97
  (s) => ({
98
- oldLineExtend: s.extendData?.oldFile?.[oldLine?.lineNumber],
99
- newLineExtend: s.extendData?.newFile?.[newLine?.lineNumber],
98
+ oldLineExtend: s.extendData?.oldFile?.[oldLine?.lineNumber ?? -1],
99
+ newLineExtend: s.extendData?.newFile?.[newLine?.lineNumber ?? -1],
100
100
  }),
101
101
  [oldLine?.lineNumber, newLine?.lineNumber]
102
102
  )
@@ -6,7 +6,6 @@ import {
6
6
  hunkContentColorName,
7
7
  diffAsideWidthName,
8
8
  } from "@git-diff-view/utils";
9
- import * as React from "react";
10
9
 
11
10
  import { useSyncHeight } from "../../hooks/useSyncHeight";
12
11
  import { ExpandUp, ExpandDown, ExpandAll } from "../DiffExpand";
@@ -7,7 +7,6 @@ import {
7
7
  hunkContentColorName,
8
8
  borderColorName,
9
9
  } from "@git-diff-view/utils";
10
- import * as React from "react";
11
10
 
12
11
  import { ExpandUp, ExpandDown, ExpandAll } from "../DiffExpand";
13
12
  import { DiffModeEnum } from "../DiffView";
@@ -1,5 +1,4 @@
1
1
  import { DiffFileLineType } from "@git-diff-view/core";
2
- import * as React from "react";
3
2
 
4
3
  import { DiffSplitContentLine } from "./DiffSplitContentLineNormal_v2";
5
4
  import { DiffSplitExtendLine } from "./DiffSplitExtendLineNormal_v2";
@@ -1,5 +1,4 @@
1
1
  import { DiffFileLineType } from "@git-diff-view/core";
2
- import * as React from "react";
3
2
 
4
3
  import { DiffSplitContentLine } from "./DiffSplitContentLineWrap_v2";
5
4
  import { DiffSplitExtendLine } from "./DiffSplitExtendLineWrap_v2";
@@ -9,6 +9,7 @@ import {
9
9
  } from "@git-diff-view/utils";
10
10
  import { memo, useEffect, useRef } from "react";
11
11
  import * as React from "react";
12
+ // @ts-ignore
12
13
  import { useSyncExternalStore } from "use-sync-external-store/shim/index.js";
13
14
 
14
15
  import { useTextWidth } from "../../hooks/useTextWidth";
@@ -53,9 +54,9 @@ export const DiffSplitViewNormal = memo(({ diffFile }: { diffFile: DiffFile }) =
53
54
 
54
55
  const ref2 = useRef<HTMLDivElement>(null);
55
56
 
56
- const ref = useRef<HTMLStyleElement>();
57
+ const ref = useRef<HTMLStyleElement>(null);
57
58
 
58
- const tempRef = useRef<SplitSide>();
59
+ const tempRef = useRef<SplitSide>(undefined);
59
60
 
60
61
  const splitLineLength = Math.max(diffFile.splitLineLength, diffFile.fileLineLength);
61
62
 
@@ -84,7 +85,7 @@ export const DiffSplitViewNormal = memo(({ diffFile }: { diffFile: DiffFile }) =
84
85
 
85
86
  const width = Math.max(40, _width + 25);
86
87
 
87
- const setStyle = (side: SplitSide) => {
88
+ const setStyle = (side?: SplitSide) => {
88
89
  if (!ref.current) return;
89
90
  if (!side) {
90
91
  ref.current.textContent = "";
@@ -95,7 +96,7 @@ export const DiffSplitViewNormal = memo(({ diffFile }: { diffFile: DiffFile }) =
95
96
  };
96
97
 
97
98
  const onMouseDown: MouseEventHandler<HTMLTableSectionElement> = (e) => {
98
- let ele = e.target;
99
+ let ele: Element | null = e.target as Element;
99
100
 
100
101
  // need remove all the selection
101
102
  if (ele && ele instanceof HTMLElement && ele.nodeName === "BUTTON") {
@@ -105,11 +106,11 @@ export const DiffSplitViewNormal = memo(({ diffFile }: { diffFile: DiffFile }) =
105
106
 
106
107
  while (ele && ele instanceof HTMLElement) {
107
108
  const state = ele.getAttribute("data-state");
108
- const side = ele.getAttribute("data-side");
109
+ const side = ele.getAttribute("data-side") as unknown as SplitSide;
109
110
  if (side) {
110
- if (tempRef.current !== SplitSide[side]) {
111
- tempRef.current = SplitSide[side];
112
- setStyle(SplitSide[side]);
111
+ if (tempRef.current !== (SplitSide[side] as unknown as SplitSide)) {
112
+ tempRef.current = SplitSide[side] as unknown as SplitSide;
113
+ setStyle(SplitSide[side] as unknown as SplitSide);
113
114
  removeAllSelection();
114
115
  }
115
116
  }
@@ -2,8 +2,8 @@
2
2
  import { type DiffFile, getSplitLines } from "@git-diff-view/core";
3
3
  import { removeAllSelection, diffFontSizeName, diffAsideWidthName } from "@git-diff-view/utils";
4
4
  import { memo, useMemo, useRef } from "react";
5
- import * as React from "react";
6
5
  // SEE https://github.com/facebook/react/pull/25231
6
+ // @ts-ignore
7
7
  import { useSyncExternalStore } from "use-sync-external-store/shim/index.js";
8
8
 
9
9
  import { useTextWidth } from "../../hooks/useTextWidth";
@@ -36,7 +36,7 @@ export const DiffSplitViewWrap = memo(({ diffFile }: { diffFile: DiffFile }) =>
36
36
 
37
37
  const lines = getSplitLines(diffFile);
38
38
 
39
- const setStyle = (side: SplitSide) => {
39
+ const setStyle = (side?: SplitSide) => {
40
40
  if (!ref.current) return;
41
41
  if (!side) {
42
42
  ref.current.textContent = "";
@@ -48,7 +48,7 @@ export const DiffSplitViewWrap = memo(({ diffFile }: { diffFile: DiffFile }) =>
48
48
  };
49
49
 
50
50
  const onMouseDown: MouseEventHandler<HTMLTableSectionElement> = (e) => {
51
- let ele = e.target;
51
+ let ele: Element | null = e.target as Element;
52
52
 
53
53
  // need remove all the selection
54
54
  if (ele && ele instanceof HTMLElement && ele.nodeName === "BUTTON") {
@@ -58,9 +58,9 @@ export const DiffSplitViewWrap = memo(({ diffFile }: { diffFile: DiffFile }) =>
58
58
 
59
59
  while (ele && ele instanceof HTMLElement) {
60
60
  const state = ele.getAttribute("data-state");
61
- const side = ele.getAttribute("data-side");
61
+ const side = ele.getAttribute("data-side") as unknown as SplitSide;
62
62
  if (side) {
63
- setStyle(SplitSide[side]);
63
+ setStyle(SplitSide[side] as unknown as SplitSide);
64
64
  removeAllSelection();
65
65
  }
66
66
  if (state) {
@@ -1,5 +1,4 @@
1
1
  import { memo, useEffect, useMemo, useRef } from "react";
2
- import * as React from "react";
3
2
 
4
3
  import { useDiffViewContext } from "../DiffViewContext";
5
4
  import { DiffWidgetContext } from "../DiffWidgetContext";