Skip to content

Commit 2c793a6

Browse files
improve
1 parent 6dea0db commit 2c793a6

File tree

11 files changed

+324
-290
lines changed

11 files changed

+324
-290
lines changed

packages/react/src/components/DiffSplitViewNormal.tsx

Lines changed: 46 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
borderColorName,
88
diffAsideWidthName,
99
} from "@git-diff-view/utils";
10-
import { Fragment, memo, useEffect, useRef, useState } from "react";
10+
import { Fragment, memo, useEffect, useRef } from "react";
1111
import * as React from "react";
1212
import { useSyncExternalStore } from "use-sync-external-store/shim/index.js";
1313

@@ -27,42 +27,18 @@ const DiffSplitViewTable = ({
2727
diffFile,
2828
enableAddWidget,
2929
enableHighlight,
30-
onSelect,
30+
onMouseDown,
3131
}: {
3232
side: SplitSide;
3333
diffFile: DiffFile;
3434
enableHighlight: boolean;
3535
enableAddWidget: boolean;
36-
onSelect?: (side: SplitSide) => void;
36+
onMouseDown?: MouseEventHandler<HTMLTableSectionElement>;
3737
}) => {
3838
const className = side === SplitSide.new ? "new-diff-table" : "old-diff-table";
3939

4040
const lines = getSplitContentLines(diffFile);
4141

42-
const onMouseDown: MouseEventHandler<HTMLTableSectionElement> = (e) => {
43-
let ele = e.target as HTMLElement;
44-
45-
if (ele && ele?.nodeName === "BUTTON") {
46-
removeAllSelection();
47-
return;
48-
}
49-
50-
while (ele && ele instanceof HTMLElement) {
51-
const state = ele.getAttribute("data-state");
52-
if (state) {
53-
if (state === "extend" || state === "hunk" || state === "widget") {
54-
onSelect?.(undefined);
55-
removeAllSelection();
56-
} else {
57-
onSelect?.(side);
58-
removeAllSelection();
59-
}
60-
return;
61-
}
62-
ele = ele.parentElement;
63-
}
64-
};
65-
6642
return (
6743
<table className={className + " w-full border-collapse border-spacing-0"} data-mode={SplitSide[side]}>
6844
<colgroup>
@@ -107,7 +83,7 @@ export const DiffSplitViewNormal = memo(({ diffFile }: { diffFile: DiffFile }) =
10783

10884
const ref2 = useRef<HTMLDivElement>(null);
10985

110-
const [side, setSide] = useState<SplitSide>();
86+
const ref = useRef<HTMLStyleElement>();
11187

11288
const splitLineLength = Math.max(diffFile.splitLineLength, diffFile.fileLineLength);
11389

@@ -140,13 +116,49 @@ export const DiffSplitViewNormal = memo(({ diffFile }: { diffFile: DiffFile }) =
140116

141117
const width = Math.max(40, _width + 25);
142118

143-
const id = `diff-root${diffFile.getId()}`
119+
const setStyle = (side: SplitSide) => {
120+
if (!ref.current) return;
121+
if (!side) {
122+
ref.current.textContent = "";
123+
} else {
124+
const id = `diff-root${diffFile.getId()}`;
125+
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}`;
126+
}
127+
};
128+
129+
const onMouseDown: MouseEventHandler<HTMLTableSectionElement> = (e) => {
130+
let ele = e.target;
131+
132+
// need remove all the selection
133+
if (ele && ele instanceof HTMLElement && ele.nodeName === "BUTTON") {
134+
removeAllSelection();
135+
return;
136+
}
137+
138+
while (ele && ele instanceof HTMLElement) {
139+
const state = ele.getAttribute("data-state");
140+
const side = ele.getAttribute("data-side");
141+
if (side) {
142+
setStyle(SplitSide[side]);
143+
removeAllSelection();
144+
}
145+
if (state) {
146+
if (state === "extend" || state === "hunk" || state === "widget") {
147+
setStyle(undefined);
148+
removeAllSelection();
149+
return;
150+
} else {
151+
return;
152+
}
153+
}
154+
155+
ele = ele.parentElement;
156+
}
157+
};
144158

145159
return (
146160
<div className="split-diff-view split-diff-view-normal flex w-full basis-[50%]">
147-
<style data-select-style>
148-
{side ? `#${id} [data-state="extend"] {user-select: none} \n #${id} [data-state="hunk"] {user-select: none} \n #${id} [data-state="widget"] {user-select: none}` : ''}
149-
</style>
161+
<style data-select-style ref={ref} />
150162
<div
151163
className="old-diff-table-wrapper diff-table-scroll-container w-full overflow-x-auto overflow-y-hidden"
152164
ref={ref1}
@@ -163,7 +175,7 @@ export const DiffSplitViewNormal = memo(({ diffFile }: { diffFile: DiffFile }) =
163175
diffFile={diffFile}
164176
enableAddWidget={enableAddWidget}
165177
enableHighlight={enableHighlight}
166-
onSelect={setSide}
178+
onMouseDown={onMouseDown}
167179
/>
168180
</div>
169181
<div className="diff-split-line w-[1.5px]" style={{ backgroundColor: `var(${borderColorName})` }} />
@@ -183,7 +195,7 @@ export const DiffSplitViewNormal = memo(({ diffFile }: { diffFile: DiffFile }) =
183195
diffFile={diffFile}
184196
enableAddWidget={enableAddWidget}
185197
enableHighlight={enableHighlight}
186-
onSelect={setSide}
198+
onMouseDown={onMouseDown}
187199
/>
188200
</div>
189201
</div>

packages/react/src/components/DiffSplitViewWrap.tsx

Lines changed: 29 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable @typescript-eslint/ban-ts-comment */
22
import { type DiffFile, getSplitContentLines } from "@git-diff-view/core";
33
import { diffAsideWidthName, diffFontSizeName, removeAllSelection } from "@git-diff-view/utils";
4-
import { Fragment, memo, useCallback, useMemo } from "react";
4+
import { Fragment, memo, useMemo, useRef } from "react";
55
import * as React from "react";
66
// SEE https://github.com/facebook/react/pull/25231
77
import { useSyncExternalStore } from "use-sync-external-store/shim/index.js";
@@ -14,37 +14,15 @@ import { DiffSplitExtendLine } from "./DiffSplitExtendLineWrap";
1414
import { DiffSplitHunkLine } from "./DiffSplitHunkLineWrap";
1515
import { DiffSplitWidgetLine } from "./DiffSplitWidgetLineWrap";
1616
import { useDiffViewContext } from "./DiffViewContext";
17-
import { createDiffSplitConfigStore } from "./tools";
1817

1918
import type { MouseEventHandler } from "react";
20-
import type { Ref, UseSelectorWithStore } from "reactivity-store";
21-
22-
const Style = ({
23-
useSelector,
24-
id,
25-
}: {
26-
useSelector: UseSelectorWithStore<{ splitRef: Ref<SplitSide> }>;
27-
id: string;
28-
}) => {
29-
const splitRef = useSelector((s) => s.splitRef);
30-
31-
return (
32-
<style data-select-style>
33-
{splitRef === SplitSide.old
34-
? `#${id} [data-side="${SplitSide[SplitSide.new]}"] {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}`
35-
: splitRef === SplitSide.new
36-
? `#${id} [data-side="${SplitSide[SplitSide.old]}"] {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}`
37-
: ""}
38-
</style>
39-
);
40-
};
4119

4220
export const DiffSplitViewWrap = memo(({ diffFile }: { diffFile: DiffFile }) => {
4321
const splitLineLength = Math.max(diffFile.splitLineLength, diffFile.fileLineLength);
4422

4523
const { useDiffContext } = useDiffViewContext();
4624

47-
const useSplitConfig = useMemo(() => createDiffSplitConfigStore(), []);
25+
const ref = useRef<HTMLStyleElement>(null);
4826

4927
const { fontSize, enableAddWidget, enableHighlight } = useDiffContext.useShallowStableSelector((s) => ({
5028
fontSize: s.fontSize,
@@ -54,10 +32,30 @@ export const DiffSplitViewWrap = memo(({ diffFile }: { diffFile: DiffFile }) =>
5432

5533
useSyncExternalStore(diffFile.subscribe, diffFile.getUpdateCount, diffFile.getUpdateCount);
5634

57-
const onMouseDown = useCallback<MouseEventHandler<HTMLTableSectionElement>>((e) => {
58-
let ele = e.target;
35+
const font = useMemo(() => ({ fontSize: fontSize + "px", fontFamily: "Menlo, Consolas, monospace" }), [fontSize]);
5936

60-
const setSelectSide = useSplitConfig.getReadonlyState().setSplit;
37+
const _width = useTextWidth({
38+
text: splitLineLength.toString(),
39+
font,
40+
});
41+
42+
const width = Math.max(40, _width + 25);
43+
44+
const lines = getSplitContentLines(diffFile);
45+
46+
const setStyle = (side: SplitSide) => {
47+
if (!ref.current) return;
48+
if (!side) {
49+
ref.current.textContent = "";
50+
} else {
51+
const id = `diff-root${diffFile.getId()}`;
52+
const targetSide = side === SplitSide.old ? SplitSide.new : SplitSide.old;
53+
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}`;
54+
}
55+
};
56+
57+
const onMouseDown: MouseEventHandler<HTMLTableSectionElement> = (e) => {
58+
let ele = e.target;
6159

6260
// need remove all the selection
6361
if (ele && ele instanceof HTMLElement && ele.nodeName === "BUTTON") {
@@ -69,12 +67,12 @@ export const DiffSplitViewWrap = memo(({ diffFile }: { diffFile: DiffFile }) =>
6967
const state = ele.getAttribute("data-state");
7068
const side = ele.getAttribute("data-side");
7169
if (side) {
72-
setSelectSide(SplitSide[side]);
70+
setStyle(SplitSide[side]);
7371
removeAllSelection();
7472
}
7573
if (state) {
7674
if (state === "extend" || state === "hunk" || state === "widget") {
77-
setSelectSide(undefined);
75+
setStyle(undefined);
7876
removeAllSelection();
7977
return;
8078
} else {
@@ -84,19 +82,7 @@ export const DiffSplitViewWrap = memo(({ diffFile }: { diffFile: DiffFile }) =>
8482

8583
ele = ele.parentElement;
8684
}
87-
// eslint-disable-next-line react-hooks/exhaustive-deps
88-
}, []);
89-
90-
const font = useMemo(() => ({ fontSize: fontSize + "px", fontFamily: "Menlo, Consolas, monospace" }), [fontSize]);
91-
92-
const _width = useTextWidth({
93-
text: splitLineLength.toString(),
94-
font,
95-
});
96-
97-
const width = Math.max(40, _width + 25);
98-
99-
const lines = getSplitContentLines(diffFile);
85+
};
10086

10187
return (
10288
<div className="split-diff-view split-diff-view-wrap w-full">
@@ -109,7 +95,7 @@ export const DiffSplitViewWrap = memo(({ diffFile }: { diffFile: DiffFile }) =>
10995
fontSize: `var(${diffFontSizeName})`,
11096
}}
11197
>
112-
<Style useSelector={useSplitConfig} id={`diff-root${diffFile.getId()}`} />
98+
<style data-select-style ref={ref} />
11399
<table className="diff-table w-full table-fixed border-collapse border-spacing-0">
114100
<colgroup>
115101
<col className="diff-table-old-num-col" width={Math.round(width)} />

packages/react/src/components/DiffUnifiedView.tsx

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* eslint-disable @typescript-eslint/ban-ts-comment */
2-
import { getUnifiedContentLine } from "@git-diff-view/core";
2+
import { getUnifiedContentLine, SplitSide } from "@git-diff-view/core";
33
import { diffFontSizeName, removeAllSelection, diffAsideWidthName } from "@git-diff-view/utils";
44
import * as React from "react";
55
import { Fragment, memo, useEffect, useMemo, useRef } from "react";
@@ -18,19 +18,11 @@ import { createDiffWidgetStore } from "./tools";
1818
import type { DiffFile } from "@git-diff-view/core";
1919
import type { MouseEventHandler } from "react";
2020

21-
const onMouseDown: MouseEventHandler<HTMLTableSectionElement> = (e) => {
22-
const ele = e.target;
23-
24-
// need remove all the selection
25-
if (ele && ele instanceof HTMLElement && ele.nodeName === "BUTTON") {
26-
removeAllSelection();
27-
return;
28-
}
29-
};
30-
3121
export const DiffUnifiedView = memo(({ diffFile }: { diffFile: DiffFile }) => {
3222
const { useDiffContext } = useDiffViewContext();
3323

24+
const ref = useRef<HTMLStyleElement>(null);
25+
3426
const useDiffContextRef = useRef(useDiffContext);
3527

3628
useDiffContextRef.current = useDiffContext;
@@ -66,9 +58,47 @@ export const DiffUnifiedView = memo(({ diffFile }: { diffFile: DiffFile }) => {
6658

6759
const lines = getUnifiedContentLine(diffFile);
6860

61+
const setStyle = (side: SplitSide) => {
62+
if (!ref.current) return;
63+
if (!side) {
64+
ref.current.textContent = "";
65+
} else {
66+
const id = `diff-root${diffFile.getId()}`;
67+
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}`;
68+
}
69+
};
70+
71+
const onMouseDown: MouseEventHandler<HTMLTableSectionElement> = (e) => {
72+
let ele = e.target;
73+
74+
// need remove all the selection
75+
if (ele && ele instanceof HTMLElement && ele.nodeName === "BUTTON") {
76+
removeAllSelection();
77+
return;
78+
}
79+
80+
while (ele && ele instanceof HTMLElement) {
81+
const state = ele.getAttribute("data-state");
82+
if (state) {
83+
if (state === "extend" || state === "hunk" || state === "widget") {
84+
setStyle(undefined);
85+
removeAllSelection();
86+
return;
87+
} else {
88+
setStyle(SplitSide.new);
89+
removeAllSelection();
90+
return;
91+
}
92+
}
93+
94+
ele = ele.parentElement;
95+
}
96+
};
97+
6998
return (
7099
<DiffWidgetContext.Provider value={contextValue}>
71100
<div className={`unified-diff-view ${enableWrap ? "unified-diff-view-wrap" : "unified-diff-view-normal"} w-full`}>
101+
<style data-select-style ref={ref} />
72102
<div
73103
className="unified-diff-table-wrapper diff-table-scroll-container w-full overflow-x-auto overflow-y-hidden"
74104
style={{

packages/react/src/components/tools.ts

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { flushSync } from "react-dom";
21
import { createStore, ref } from "reactivity-store";
32

43
import type { DiffModeEnum, DiffViewProps, SplitSide } from "./DiffView";
@@ -124,17 +123,3 @@ export const createDiffWidgetStore = (useDiffContextRef: RefObject<ReturnType<ty
124123
return { widgetSide, widgetLineNumber, setWidget };
125124
});
126125
};
127-
128-
export const createDiffSplitConfigStore = () => {
129-
return createStore(() => {
130-
const splitRef = ref<SplitSide>(undefined);
131-
132-
const setSplit = (side: SplitSide | undefined) => {
133-
flushSync(() => {
134-
splitRef.value = side;
135-
});
136-
};
137-
138-
return { splitRef, setSplit };
139-
});
140-
};

0 commit comments

Comments
 (0)