Highlight currently selected file (#1725)

* Highlight selected file

* Don't recompute context on every render

* Fix lint errors
This commit is contained in:
Jim Su 2024-05-11 17:36:55 -04:00 committed by GitHub
parent c997289eb7
commit 3abdc231c4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 62 additions and 39 deletions

View File

@ -1,7 +1,7 @@
import Editor, { Monaco } from "@monaco-editor/react";
import { Tab, Tabs } from "@nextui-org/react";
import type { editor } from "monaco-editor";
import React, { useState } from "react";
import React, { useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { VscCode } from "react-icons/vsc";
import { useDispatch, useSelector } from "react-redux";
@ -10,10 +10,19 @@ import { selectFile } from "#/services/fileService";
import { setCode } from "#/state/codeSlice";
import { RootState } from "#/store";
import FileExplorer from "./file-explorer/FileExplorer";
import { CodeEditorContext } from "./CodeEditorContext";
function CodeEditor(): JSX.Element {
const { t } = useTranslation();
const [selectedFileName, setSelectedFileName] = useState("");
const [selectedFileAbsolutePath, setSelectedFileAbsolutePath] = useState("");
const selectedFileName = useMemo(() => {
const paths = selectedFileAbsolutePath.split("/");
return paths[paths.length - 1];
}, [selectedFileAbsolutePath]);
const codeEditorContext = useMemo(
() => ({ selectedFileAbsolutePath }),
[selectedFileAbsolutePath],
);
const dispatch = useDispatch();
const code = useSelector((state: RootState) => state.code.code);
@ -38,10 +47,9 @@ function CodeEditor(): JSX.Element {
const onSelectFile = async (absolutePath: string) => {
const paths = absolutePath.split("/");
const fileName = paths[paths.length - 1];
const rootlessPath = paths.slice(1).join("/");
setSelectedFileName(fileName);
setSelectedFileAbsolutePath(absolutePath);
const newCode = await selectFile(rootlessPath);
dispatch(setCode(newCode));
@ -49,42 +57,44 @@ function CodeEditor(): JSX.Element {
return (
<div className="flex h-full w-full bg-neutral-900 transition-all duration-500 ease-in-out">
<FileExplorer onFileClick={onSelectFile} />
<div className="flex flex-col min-h-0 w-full">
<Tabs
disableCursorAnimation
classNames={{
base: "border-b border-divider border-neutral-600 mb-4",
tabList:
"w-full relative rounded-none bg-neutral-900 p-0 border-divider",
cursor: "w-full bg-neutral-600 rounded-none",
tab: "max-w-fit px-4 h-[36px]",
tabContent: "group-data-[selected=true]:text-white",
}}
aria-label="Options"
>
<Tab
key={selectedFileName.toLocaleLowerCase()}
title={selectedFileName}
/>
</Tabs>
<div className="flex grow items-center justify-center">
{selectedFileName === "" ? (
<div className="flex flex-col items-center text-neutral-400">
<VscCode size={100} />
{t(I18nKey.CODE_EDITOR$EMPTY_MESSAGE)}
</div>
) : (
<Editor
height="100%"
path={selectedFileName.toLocaleLowerCase()}
defaultValue=""
value={code}
onMount={handleEditorDidMount}
<CodeEditorContext.Provider value={codeEditorContext}>
<FileExplorer onFileClick={onSelectFile} />
<div className="flex flex-col min-h-0 w-full">
<Tabs
disableCursorAnimation
classNames={{
base: "border-b border-divider border-neutral-600 mb-4",
tabList:
"w-full relative rounded-none bg-neutral-900 p-0 border-divider",
cursor: "w-full bg-neutral-600 rounded-none",
tab: "max-w-fit px-4 h-[36px]",
tabContent: "group-data-[selected=true]:text-white",
}}
aria-label="Options"
>
<Tab
key={selectedFileName.toLocaleLowerCase()}
title={selectedFileName}
/>
)}
</Tabs>
<div className="flex grow items-center justify-center">
{selectedFileName === "" ? (
<div className="flex flex-col items-center text-neutral-400">
<VscCode size={100} />
{t(I18nKey.CODE_EDITOR$EMPTY_MESSAGE)}
</div>
) : (
<Editor
height="100%"
path={selectedFileName.toLocaleLowerCase()}
defaultValue=""
value={code}
onMount={handleEditorDidMount}
/>
)}
</div>
</div>
</div>
</CodeEditorContext.Provider>
</div>
);
}

View File

@ -0,0 +1,5 @@
import { createContext } from "react";
export const CodeEditorContext = createContext({
selectedFileAbsolutePath: "",
});

View File

@ -1,7 +1,9 @@
import React from "react";
import { twMerge } from "tailwind-merge";
import FolderIcon from "../FolderIcon";
import FileIcon from "../FileIcons";
import { WorkspaceFile } from "#/services/fileService";
import { CodeEditorContext } from "../CodeEditorContext";
interface TitleProps {
name: string;
@ -37,6 +39,7 @@ function TreeNode({
defaultOpen = false,
}: TreeNodeProps) {
const [isOpen, setIsOpen] = React.useState(defaultOpen);
const { selectedFileAbsolutePath } = React.useContext(CodeEditorContext);
const handleClick = React.useCallback(() => {
if (node.children) {
@ -47,7 +50,12 @@ function TreeNode({
}, [node, path, onFileClick]);
return (
<div className="text-sm text-neutral-400">
<div
className={twMerge(
"text-sm text-neutral-400",
path === selectedFileAbsolutePath ? "bg-gray-700" : "",
)}
>
<Title
name={node.name}
type={node.children ? "folder" : "file"}