mirror of
https://github.com/silvanocerza/github-gitless-sync.git
synced 2026-07-22 12:10:28 +00:00
Add tab bar to select files with conflicts
This commit is contained in:
parent
0721aa9d90
commit
c315378fb7
2 changed files with 223 additions and 95 deletions
|
|
@ -7,6 +7,7 @@ import { createDiffHighlightPlugin } from "./diff-highlight-plugin";
|
|||
import EditorPane from "./editor-pane";
|
||||
import ActionsGutter from "./actions-gutter";
|
||||
import * as React from "react";
|
||||
import FilesTabBar from "./files-tab-bar";
|
||||
|
||||
// Add styles for diff highlighting
|
||||
const styles = document.createElement("style");
|
||||
|
|
@ -53,105 +54,117 @@ const DiffView: React.FC<DiffViewProps> = ({
|
|||
return (
|
||||
<div
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
overflow: "hidden",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<div style={{ flex: 1, overflow: "hidden" }}>
|
||||
<EditorPane
|
||||
content={oldText}
|
||||
highlightPluginSpec={{
|
||||
diff: diffs,
|
||||
isOriginal: true,
|
||||
}}
|
||||
onEditorUpdate={handleEditorReady}
|
||||
onContentChange={onOldTextChange}
|
||||
onScrollTopUpdate={setLeftEditorTopOffset}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ minWidth: "160px", width: "auto" }}>
|
||||
<ActionsGutter
|
||||
diffChunks={diffs}
|
||||
lineHeight={lineHeight}
|
||||
leftEditorTopOffset={leftEditorTopOffset}
|
||||
rightEditorTopLineOffset={rightEditorTopOffset}
|
||||
onAcceptLeft={(chunk: DiffChunk) => {
|
||||
if (chunk.type === "add") {
|
||||
const oldLines = oldText.split("\n");
|
||||
oldLines.splice(
|
||||
chunk.startLeftLine - 1,
|
||||
0,
|
||||
...newText
|
||||
.split("\n")
|
||||
.slice(chunk.startRightLine - 1, chunk.endRightLine - 1),
|
||||
);
|
||||
onOldTextChange(oldLines.join("\n"));
|
||||
} else if (chunk.type === "modify") {
|
||||
const oldLines = oldText.split("\n");
|
||||
oldLines.splice(
|
||||
chunk.startLeftLine - 1,
|
||||
chunk.endLeftLine - chunk.startLeftLine,
|
||||
...newText
|
||||
.split("\n")
|
||||
.slice(chunk.startRightLine - 1, chunk.endRightLine - 1),
|
||||
);
|
||||
onOldTextChange(oldLines.join("\n"));
|
||||
}
|
||||
}}
|
||||
onAcceptRight={(chunk: DiffChunk) => {
|
||||
if (chunk.type === "remove") {
|
||||
const newLines = newText.split("\n");
|
||||
newLines.splice(
|
||||
chunk.startRightLine - 1,
|
||||
0,
|
||||
...oldText
|
||||
.split("\n")
|
||||
.slice(chunk.startLeftLine - 1, chunk.endLeftLine - 1),
|
||||
);
|
||||
onNewTextChange(newLines.join("\n"));
|
||||
} else if (chunk.type === "modify") {
|
||||
const newLines = newText.split("\n");
|
||||
newLines.splice(
|
||||
chunk.startRightLine - 1,
|
||||
chunk.endRightLine - chunk.startRightLine,
|
||||
...oldText
|
||||
.split("\n")
|
||||
.slice(chunk.startLeftLine - 1, chunk.endLeftLine - 1),
|
||||
);
|
||||
onNewTextChange(newLines.join("\n"));
|
||||
}
|
||||
}}
|
||||
onReject={(chunk: DiffChunk) => {
|
||||
if (chunk.type === "add") {
|
||||
const newLines = newText.split("\n");
|
||||
newLines.splice(
|
||||
chunk.startRightLine - 1,
|
||||
chunk.endRightLine - chunk.startRightLine,
|
||||
);
|
||||
onNewTextChange(newLines.join("\n"));
|
||||
} else if (chunk.type === "remove") {
|
||||
const oldLines = oldText.split("\n");
|
||||
oldLines.splice(
|
||||
chunk.startLeftLine - 1,
|
||||
chunk.endLeftLine - chunk.startLeftLine,
|
||||
);
|
||||
onOldTextChange(oldLines.join("\n"));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ flex: 1, overflow: "hidden" }}>
|
||||
<EditorPane
|
||||
content={newText}
|
||||
highlightPluginSpec={{
|
||||
diff: diffs,
|
||||
isOriginal: false,
|
||||
}}
|
||||
onContentChange={onNewTextChange}
|
||||
onScrollTopUpdate={setRightEditorTopOffset}
|
||||
/>
|
||||
<FilesTabBar
|
||||
files={["this", "that", "those"]}
|
||||
onTabChange={(filename: string) => console.log(`Clicked ${filename}`)}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
<div style={{ flex: 1, overflow: "hidden" }}>
|
||||
<EditorPane
|
||||
content={oldText}
|
||||
highlightPluginSpec={{
|
||||
diff: diffs,
|
||||
isOriginal: true,
|
||||
}}
|
||||
onEditorUpdate={handleEditorReady}
|
||||
onContentChange={onOldTextChange}
|
||||
onScrollTopUpdate={setLeftEditorTopOffset}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ minWidth: "160px", width: "auto" }}>
|
||||
<ActionsGutter
|
||||
diffChunks={diffs}
|
||||
lineHeight={lineHeight}
|
||||
leftEditorTopOffset={leftEditorTopOffset}
|
||||
rightEditorTopLineOffset={rightEditorTopOffset}
|
||||
onAcceptLeft={(chunk: DiffChunk) => {
|
||||
if (chunk.type === "add") {
|
||||
const oldLines = oldText.split("\n");
|
||||
oldLines.splice(
|
||||
chunk.startLeftLine - 1,
|
||||
0,
|
||||
...newText
|
||||
.split("\n")
|
||||
.slice(chunk.startRightLine - 1, chunk.endRightLine - 1),
|
||||
);
|
||||
onOldTextChange(oldLines.join("\n"));
|
||||
} else if (chunk.type === "modify") {
|
||||
const oldLines = oldText.split("\n");
|
||||
oldLines.splice(
|
||||
chunk.startLeftLine - 1,
|
||||
chunk.endLeftLine - chunk.startLeftLine,
|
||||
...newText
|
||||
.split("\n")
|
||||
.slice(chunk.startRightLine - 1, chunk.endRightLine - 1),
|
||||
);
|
||||
onOldTextChange(oldLines.join("\n"));
|
||||
}
|
||||
}}
|
||||
onAcceptRight={(chunk: DiffChunk) => {
|
||||
if (chunk.type === "remove") {
|
||||
const newLines = newText.split("\n");
|
||||
newLines.splice(
|
||||
chunk.startRightLine - 1,
|
||||
0,
|
||||
...oldText
|
||||
.split("\n")
|
||||
.slice(chunk.startLeftLine - 1, chunk.endLeftLine - 1),
|
||||
);
|
||||
onNewTextChange(newLines.join("\n"));
|
||||
} else if (chunk.type === "modify") {
|
||||
const newLines = newText.split("\n");
|
||||
newLines.splice(
|
||||
chunk.startRightLine - 1,
|
||||
chunk.endRightLine - chunk.startRightLine,
|
||||
...oldText
|
||||
.split("\n")
|
||||
.slice(chunk.startLeftLine - 1, chunk.endLeftLine - 1),
|
||||
);
|
||||
onNewTextChange(newLines.join("\n"));
|
||||
}
|
||||
}}
|
||||
onReject={(chunk: DiffChunk) => {
|
||||
if (chunk.type === "add") {
|
||||
const newLines = newText.split("\n");
|
||||
newLines.splice(
|
||||
chunk.startRightLine - 1,
|
||||
chunk.endRightLine - chunk.startRightLine,
|
||||
);
|
||||
onNewTextChange(newLines.join("\n"));
|
||||
} else if (chunk.type === "remove") {
|
||||
const oldLines = oldText.split("\n");
|
||||
oldLines.splice(
|
||||
chunk.startLeftLine - 1,
|
||||
chunk.endLeftLine - chunk.startLeftLine,
|
||||
);
|
||||
onOldTextChange(oldLines.join("\n"));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ flex: 1, overflow: "hidden" }}>
|
||||
<EditorPane
|
||||
content={newText}
|
||||
highlightPluginSpec={{
|
||||
diff: diffs,
|
||||
isOriginal: false,
|
||||
}}
|
||||
onContentChange={onNewTextChange}
|
||||
onScrollTopUpdate={setRightEditorTopOffset}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
115
src/views/conflicts-resolution/files-tab-bar.tsx
Normal file
115
src/views/conflicts-resolution/files-tab-bar.tsx
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
import { Menu } from "obsidian";
|
||||
import * as React from "react";
|
||||
|
||||
interface FilesTabBarProps {
|
||||
files: string[];
|
||||
onTabChange: (filename: string) => void;
|
||||
}
|
||||
|
||||
const FilesTabBar: React.FC<FilesTabBarProps> = ({ files, onTabChange }) => {
|
||||
const [currentFile, setCurrentFile] = React.useState<string>(
|
||||
files.at(0) || "",
|
||||
);
|
||||
|
||||
const onTabClick = (filename: string) => {
|
||||
if (filename === currentFile) {
|
||||
return;
|
||||
}
|
||||
setCurrentFile(filename);
|
||||
onTabChange(filename);
|
||||
};
|
||||
|
||||
const createTab = (filename: string) => {
|
||||
return (
|
||||
<div
|
||||
className={`workspace-tab-header tappable ${filename === currentFile ? "is-active mod-active" : ""}`}
|
||||
aria-label={`${filename}`}
|
||||
data-tooltip-delay="300"
|
||||
onClick={() => onTabClick(filename)}
|
||||
>
|
||||
<div className="workspace-tab-header-inner">
|
||||
<div>{filename}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Makes it easier to position the tab bar menu
|
||||
const divRef = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
backgroundColor: "var(--tab-container-background)",
|
||||
height: "var(--header-height)",
|
||||
borderBottom: "var(--tab-outline-width) solid var(--tab-outline-color)",
|
||||
flex: "0 0 auto",
|
||||
paddingLeft: "var(--size-4-2)",
|
||||
paddingRight: "var(--size-4-2)",
|
||||
position: "relative",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
animationDuration: "250ms",
|
||||
display: "flex",
|
||||
flex: "0 1 auto",
|
||||
overflow: "auto",
|
||||
padding: "1px 0 7px",
|
||||
margin: "6px 0 0 0",
|
||||
gap: "3px",
|
||||
}}
|
||||
>
|
||||
{files.map(createTab)}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexGrow: 1,
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
ref={divRef}
|
||||
style={{
|
||||
marginInlineEnd: "var(--size-4-1)",
|
||||
padding: "var(--size-4-2) 0 var(--size-2-3)",
|
||||
}}
|
||||
onClick={() => {
|
||||
const menu = new Menu();
|
||||
files.forEach((filename: string) => {
|
||||
menu.addItem((item) => {
|
||||
item.setTitle(filename).onClick(() => onTabClick(filename));
|
||||
});
|
||||
});
|
||||
// We use the divRef to force the position to be relative to this div. We want the position
|
||||
// to always be the same so using the click event is not feasible as that depends on the
|
||||
// coordinates of the user click.
|
||||
// The event target is not usable either as depending where the user clicks the target
|
||||
// might be this div or its children thus having different positions.
|
||||
const rect = divRef.current!.getBoundingClientRect();
|
||||
menu.showAtPosition({ x: rect.left, y: rect.bottom });
|
||||
}}
|
||||
>
|
||||
<span className="clickable-icon">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="svg-icon lucide-chevron-down"
|
||||
>
|
||||
<path d="m6 9 6 6 6-6"></path>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FilesTabBar;
|
||||
Loading…
Reference in a new issue