Add topbar to code editor to indicate open file + hold the close button

This commit is contained in:
bkellam 2024-08-26 21:40:08 -07:00
parent 87ad7ef981
commit e7616b43ad

View file

@ -5,8 +5,8 @@ import { Separator } from "@/components/ui/separator";
import { useNonEmptyQueryParam } from "@/hooks/useNonEmptyQueryParam"; import { useNonEmptyQueryParam } from "@/hooks/useNonEmptyQueryParam";
import { defaultKeymap } from "@codemirror/commands"; import { defaultKeymap } from "@codemirror/commands";
import { javascript } from "@codemirror/lang-javascript"; import { javascript } from "@codemirror/lang-javascript";
import { keymap } from "@codemirror/view"; import { EditorView, keymap, ViewPlugin, ViewUpdate } from "@codemirror/view";
import { SymbolIcon } from "@radix-ui/react-icons"; import { SymbolIcon, FileIcon, Cross1Icon } from "@radix-ui/react-icons";
import { ScrollArea, Scrollbar } from "@radix-ui/react-scroll-area"; import { ScrollArea, Scrollbar } from "@radix-ui/react-scroll-area";
import CodeMirror from '@uiw/react-codemirror'; import CodeMirror from '@uiw/react-codemirror';
import Image from "next/image"; import Image from "next/image";
@ -24,6 +24,7 @@ import { GetSourceResponse, pathQueryParamName, repoQueryParamName } from "@/lib
import { createPathWithQueryParams } from "@/lib/utils"; import { createPathWithQueryParams } from "@/lib/utils";
import { ThemeSelectorButton } from "./themeSelectorButton"; import { ThemeSelectorButton } from "./themeSelectorButton";
import { useTheme } from "next-themes"; import { useTheme } from "next-themes";
import { Button } from "@/components/ui/button";
interface ZoekMatch { interface ZoekMatch {
URL: string, URL: string,
@ -67,6 +68,7 @@ export default function Home() {
const [isCodePanelOpen, setIsCodePanelOpen] = useState(false); const [isCodePanelOpen, setIsCodePanelOpen] = useState(false);
const [code, setCode] = useState(""); const [code, setCode] = useState("");
const [filepath, setFilepath] = useState("");
const [fileMatches, setFileMatches] = useState<ZoekFileMatch[]>([]); const [fileMatches, setFileMatches] = useState<ZoekFileMatch[]>([]);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
@ -138,6 +140,7 @@ export default function Home() {
.then((body: GetSourceResponse) => { .then((body: GetSourceResponse) => {
setIsCodePanelOpen(true); setIsCodePanelOpen(true);
setCode(body.content); setCode(body.content);
setFilepath(match.FileName);
}); });
}} }}
/> />
@ -153,6 +156,8 @@ export default function Home() {
> >
<CodeEditor <CodeEditor
code={code} code={code}
filepath={filepath}
onClose={() => setIsCodePanelOpen(false)}
/> />
</ResizablePanel> </ResizablePanel>
)} )}
@ -163,10 +168,14 @@ export default function Home() {
interface CodeEditorProps { interface CodeEditorProps {
code: string; code: string;
filepath: string;
onClose: () => void;
} }
const CodeEditor = ({ const CodeEditor = ({
code, code,
filepath,
onClose,
}: CodeEditorProps) => { }: CodeEditorProps) => {
const { theme: _theme, systemTheme } = useTheme(); const { theme: _theme, systemTheme } = useTheme();
const theme = useMemo(() => { const theme = useMemo(() => {
@ -177,19 +186,62 @@ const CodeEditor = ({
return _theme ?? "light"; return _theme ?? "light";
}, [_theme]); }, [_theme]);
const [gutterWidth, setGutterWidth] = useState(0);
const gutterWidthPlugin = useMemo(() => {
return ViewPlugin.fromClass(class {
width: number = 0;
constructor(view: EditorView) {
this.measureWidth(view)
}
update(update: ViewUpdate) {
if (update.geometryChanged) this.measureWidth(update.view)
}
measureWidth(view: EditorView) {
let gutter = view.scrollDOM.querySelector('.cm-gutters') as HTMLElement
if (gutter) this.width = gutter.offsetWidth
}
});
}, []);
return ( return (
<ScrollArea className="h-full overflow-y-auto"> <div className="h-full">
<CodeMirror <div className="flex flex-row bg-cyan-200 dark:bg-cyan-900 items-center justify-between pr-3">
editable={false} <div className="flex flex-row">
value={code} <div
theme={theme === "dark" ? "dark" : "light"} style={{width: `${gutterWidth}px`}}
extensions={[ className="flex justify-center items-center"
keymap.of(defaultKeymap), >
javascript(), <FileIcon className="h-4 w-4" />
]} </div>
/> <span>{filepath}</span>
<Scrollbar orientation="vertical" /> </div>
</ScrollArea> <Button variant="ghost" size="icon" className="h-6 w-6">
<Cross1Icon
className="h-4 w-4"
onClick={onClose}
/>
</Button>
</div>
<ScrollArea className="h-full overflow-y-auto">
<CodeMirror
editable={false}
value={code}
theme={theme === "dark" ? "dark" : "light"}
extensions={[
keymap.of(defaultKeymap),
javascript(),
gutterWidthPlugin.extension,
EditorView.updateListener.of(update => {
const width = update.view.plugin(gutterWidthPlugin)?.width;
if (width) {
setGutterWidth(width);
}
})
]}
/>
<Scrollbar orientation="vertical" />
</ScrollArea>
</div>
) )
} }
@ -272,7 +324,7 @@ const FileMatch = ({
<div <div
key={index} key={index}
className="font-mono px-4 py-0.5 text-sm cursor-pointer" className="font-mono px-4 py-0.5 text-sm cursor-pointer"
onClick={() =>{ onClick={() => {
onOpenFile(); onOpenFile();
}} }}
> >