Commit f3329841 authored by tom's avatar tom

simple tabs

parent 0f94a75f
...@@ -6,6 +6,7 @@ import React from 'react'; ...@@ -6,6 +6,7 @@ import React from 'react';
import type { File, Monaco } from './types'; import type { File, Monaco } from './types';
import CodeEditorSideBar from './CodeEditorSideBar'; import CodeEditorSideBar from './CodeEditorSideBar';
import CodeEditorTabs from './CodeEditorTabs';
import * as themes from './utils/themes'; import * as themes from './utils/themes';
const EDITOR_OPTIONS = { const EDITOR_OPTIONS = {
...@@ -20,6 +21,7 @@ interface Props { ...@@ -20,6 +21,7 @@ interface Props {
const CodeEditor = ({ data }: Props) => { const CodeEditor = ({ data }: Props) => {
const [ instance, setInstance ] = React.useState<Monaco | undefined>(); const [ instance, setInstance ] = React.useState<Monaco | undefined>();
const [ index, setIndex ] = React.useState(0); const [ index, setIndex ] = React.useState(0);
const [ tabs, setTabs ] = React.useState([ data[index].file_path ]);
const editorRef = React.useRef<monaco.editor.IStandaloneCodeEditor>(); const editorRef = React.useRef<monaco.editor.IStandaloneCodeEditor>();
const { colorMode } = useColorMode(); const { colorMode } = useColorMode();
...@@ -45,16 +47,43 @@ const CodeEditor = ({ data }: Props) => { ...@@ -45,16 +47,43 @@ const CodeEditor = ({ data }: Props) => {
const handleSelectFile = React.useCallback((index: number, lineNumber?: number) => { const handleSelectFile = React.useCallback((index: number, lineNumber?: number) => {
setIndex(index); setIndex(index);
setTabs((prev) => prev.some((item) => item === data[index].file_path) ? prev : ([ ...prev, data[index].file_path ]));
if (lineNumber !== undefined && !Object.is(lineNumber, NaN)) { if (lineNumber !== undefined && !Object.is(lineNumber, NaN)) {
window.setTimeout(() => { window.setTimeout(() => {
editorRef.current?.revealLineInCenter(lineNumber); editorRef.current?.revealLineInCenter(lineNumber);
}, 0); }, 0);
} }
}, []); }, [ data ]);
const handleTabSelect = React.useCallback((path: string) => {
const index = data.findIndex((item) => item.file_path === path);
if (index > -1) {
setIndex(index);
}
}, [ data ]);
const handleTabClose = React.useCallback((path: string) => {
setTabs((prev) => {
if (prev.length > 1) {
const tabIndex = prev.findIndex((item) => item === path);
const isActive = data[index].file_path === path;
if (isActive) {
const nextActiveIndex = data.findIndex((item) => item.file_path === prev[Math.max(0, tabIndex - 1)]);
setIndex(nextActiveIndex);
}
return prev.filter((item) => item !== path);
}
return prev;
});
}, [ data, index ]);
return ( return (
<Flex overflow="hidden" borderRadius="md" height="500px"> <Flex overflow="hidden" borderRadius="md" height="540px">
<Box flexGrow={ 1 }> <Box flexGrow={ 1 }>
<CodeEditorTabs tabs={ tabs } activeTab={ data[index].file_path } onTabSelect={ handleTabSelect } onTabClose={ handleTabClose }/>
<MonacoEditor <MonacoEditor
language="sol" language="sol"
path={ data[index].file_path } path={ data[index].file_path }
......
...@@ -4,6 +4,7 @@ import React from 'react'; ...@@ -4,6 +4,7 @@ import React from 'react';
import type { SearchResult } from './types'; import type { SearchResult } from './types';
import CodeEditorSearchResultItem from './CodeEditorSearchResultItem'; import CodeEditorSearchResultItem from './CodeEditorSearchResultItem';
import getFileName from './utils/getFileName';
interface Props { interface Props {
data: SearchResult; data: SearchResult;
...@@ -11,7 +12,7 @@ interface Props { ...@@ -11,7 +12,7 @@ interface Props {
} }
const CodeEditorSearchSection = ({ data, onItemClick }: Props) => { const CodeEditorSearchSection = ({ data, onItemClick }: Props) => {
const fileName = data.file_path.split('/').at(-1); const fileName = getFileName(data.file_path);
const handleFileLineClick = React.useCallback((event: React.MouseEvent) => { const handleFileLineClick = React.useCallback((event: React.MouseEvent) => {
const lineNumber = Number((event.currentTarget as HTMLDivElement).getAttribute('data-line-number')); const lineNumber = Number((event.currentTarget as HTMLDivElement).getAttribute('data-line-number'));
......
import { Flex, IconButton } from '@chakra-ui/react';
import React from 'react';
import iconCross from 'icons/cross.svg';
import getFileName from './utils/getFileName';
interface Props {
isActive?: boolean;
path: string;
onClick: (path: string) => void;
onClose: (path: string) => void;
isCloseDisabled: boolean;
}
const CodeEditorTab = ({ isActive, path, onClick, onClose, isCloseDisabled }: Props) => {
const name = getFileName(path);
const handleClick = React.useCallback(() => {
onClick(path);
}, [ onClick, path ]);
const handleClose = React.useCallback((event: React.MouseEvent) => {
event.stopPropagation();
!isCloseDisabled && onClose(path);
}, [ isCloseDisabled, onClose, path ]);
return (
<Flex
py={ 1 }
pl={ 3 }
pr={ isActive ? 0 : 5 }
fontSize="sm"
lineHeight={ 6 }
borderRightWidth="1px"
borderRightColor="divider"
borderBottomWidth="1px"
borderBottomColor={ isActive ? 'deeppink' : 'divider' }
color={ isActive ? 'black' : 'gray.600' }
alignItems="center"
fontWeight={ 500 }
mb="-1px"
cursor="pointer"
onClick={ handleClick }
_hover={{
pr: '0',
svg: {
display: 'block',
},
}}
>
<span>{ name }</span>
<IconButton
as={ iconCross }
boxSize={ 5 }
p="2px"
variant="unstyled"
aria-label="close"
onClick={ handleClose }
isDisabled={ isCloseDisabled }
display={ isActive ? 'block' : 'none' }
/>
</Flex>
);
};
export default React.memo(CodeEditorTab);
import { Flex } from '@chakra-ui/react';
import React from 'react';
import CodeEditorTab from './CodeEditorTab';
interface Props {
tabs: Array<string>;
activeTab: string;
onTabSelect: (tab: string) => void;
onTabClose: (tab: string) => void;
}
const CodeEditorTabs = ({ tabs, activeTab, onTabSelect, onTabClose }: Props) => {
return (
<Flex
bgColor="lightblue"
borderBottomWidth="1px"
borderColor="divider"
flexWrap="wrap"
>
{ tabs.map((tab) => (
<CodeEditorTab
key={ tab }
path={ tab }
isActive={ activeTab === tab }
onClick={ onTabSelect }
onClose={ onTabClose }
isCloseDisabled={ tabs.length === 1 }
/>
)) }
</Flex>
);
};
export default React.memo(CodeEditorTabs);
export default function getFileName(path: string) {
const chunks = path.split('/');
return chunks[chunks.length - 1];
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment