mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
unique file names in tabs and sticky bar
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
const stripLeadingSlash = (str: string) => str[0] === '/' ? str.slice(1) : str;
|
||||
|
||||
export default stripLeadingSlash;
|
||||
@@ -81,7 +81,7 @@ const CodeEditor = ({ data }: Props) => {
|
||||
}, [ data, index ]);
|
||||
|
||||
return (
|
||||
<Flex overflow="hidden" borderRadius="md" height="540px">
|
||||
<Flex overflow="hidden" borderRadius="md" height="540px" position="relative">
|
||||
<Box flexGrow={ 1 }>
|
||||
<CodeEditorTabs tabs={ tabs } activeTab={ data[index].file_path } onTabSelect={ handleTabSelect } onTabClose={ handleTabClose }/>
|
||||
<MonacoEditor
|
||||
|
||||
@@ -29,9 +29,9 @@ const CodeEditorSideBar = ({ onFileSelect, data, monaco }: Props) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Box w="250px" flexShrink={ 0 } bgColor="lightpink" fontSize="sm" overflowY="scroll" px={ 3 } position="relative">
|
||||
<Box w="250px" flexShrink={ 0 } bgColor="lightpink" fontSize="sm" overflowY="scroll" px={ 3 }>
|
||||
<Tabs isLazy lazyBehavior="keepMounted" variant="unstyled" size="sm">
|
||||
<TabList columnGap={ 3 }>
|
||||
<TabList columnGap={ 3 } position="sticky" top={ 0 } left={ 0 } bgColor="lightcoral" zIndex="sticky">
|
||||
<Tab { ...tabProps }>Explorer</Tab>
|
||||
<Tab { ...tabProps }>Search</Tab>
|
||||
</TabList>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Flex, IconButton } from '@chakra-ui/react';
|
||||
import { Flex, IconButton, chakra } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import iconCross from 'icons/cross.svg';
|
||||
|
||||
import getFileName from './utils/getFileName';
|
||||
import getFilePathParts from './utils/getFilePathParts';
|
||||
|
||||
interface Props {
|
||||
isActive?: boolean;
|
||||
@@ -11,10 +11,11 @@ interface Props {
|
||||
onClick: (path: string) => void;
|
||||
onClose: (path: string) => void;
|
||||
isCloseDisabled: boolean;
|
||||
tabsPathChunks: Array<Array<string>>;
|
||||
}
|
||||
|
||||
const CodeEditorTab = ({ isActive, path, onClick, onClose, isCloseDisabled }: Props) => {
|
||||
const name = getFileName(path);
|
||||
const CodeEditorTab = ({ isActive, path, onClick, onClose, isCloseDisabled, tabsPathChunks }: Props) => {
|
||||
const [ fileName, folderName ] = getFilePathParts(path, tabsPathChunks);
|
||||
|
||||
const handleClick = React.useCallback(() => {
|
||||
onClick(path);
|
||||
@@ -49,7 +50,8 @@ const CodeEditorTab = ({ isActive, path, onClick, onClose, isCloseDisabled }: Pr
|
||||
},
|
||||
}}
|
||||
>
|
||||
<span>{ name }</span>
|
||||
<span>{ fileName }</span>
|
||||
{ folderName && <chakra.span fontSize="xs" color="text_secondary" ml={ 1 }>{ folderName[0] === '.' ? '' : '...' }{ folderName }</chakra.span> }
|
||||
<IconButton
|
||||
as={ iconCross }
|
||||
boxSize={ 5 }
|
||||
|
||||
@@ -11,6 +11,10 @@ interface Props {
|
||||
}
|
||||
|
||||
const CodeEditorTabs = ({ tabs, activeTab, onTabSelect, onTabClose }: Props) => {
|
||||
const tabsPathChunks = React.useMemo(() => {
|
||||
return tabs.map((tab) => tab.split('/'));
|
||||
}, [ tabs ]);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
bgColor="lightblue"
|
||||
@@ -26,6 +30,7 @@ const CodeEditorTabs = ({ tabs, activeTab, onTabSelect, onTabClose }: Props) =>
|
||||
onClick={ onTabSelect }
|
||||
onClose={ onTabClose }
|
||||
isCloseDisabled={ tabs.length === 1 }
|
||||
tabsPathChunks={ tabsPathChunks }
|
||||
/>
|
||||
)) }
|
||||
</Flex>
|
||||
|
||||
@@ -12,7 +12,7 @@ interface Props {
|
||||
const CoderEditorCollapseButton = ({ onClick, label, isDisabled }: Props) => {
|
||||
return (
|
||||
<Tooltip label={ label } isDisabled={ isDisabled }>
|
||||
<Flex position="absolute" right={ 3 } top={ 2 }>
|
||||
<Flex position="absolute" right={ 3 } top={ 2 } zIndex="sticky">
|
||||
<IconButton
|
||||
as={ iconCopy }
|
||||
boxSize={ 4 }
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { File, FileTree } from '../types';
|
||||
|
||||
import sortFileTree from './sortFileTree';
|
||||
import stripLeadingSlash from 'lib/stripLeadingSlash';
|
||||
|
||||
const stripLeadingSlash = (str: string) => str[0] === '/' ? str.slice(1) : str;
|
||||
import sortFileTree from './sortFileTree';
|
||||
|
||||
export default function composeFileTree(files: Array<File>) {
|
||||
const result: FileTree = [];
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import getFilePathParts from './getFilePathParts';
|
||||
|
||||
it('computes correct chunks if all file name are unique', () => {
|
||||
const result = getFilePathParts(
|
||||
'/src/utils/Ownable.sol',
|
||||
[
|
||||
'/src/utils/EIP712.sol'.split('/'),
|
||||
'/src/token/BaseERC20.sol'.split('/'),
|
||||
],
|
||||
);
|
||||
|
||||
expect(result).toEqual([ 'Ownable.sol', undefined ]);
|
||||
});
|
||||
|
||||
it('computes correct chunks if files with the same name is not in folders with the same name', () => {
|
||||
const result = getFilePathParts(
|
||||
'/src/utils/Ownable.sol',
|
||||
[
|
||||
'/src/utils/EIP712.sol'.split('/'),
|
||||
'/lib/_openzeppelin/contracts/contracts/access/Ownable.sol'.split('/'),
|
||||
],
|
||||
);
|
||||
|
||||
expect(result).toEqual([ 'Ownable.sol', '/utils' ]);
|
||||
});
|
||||
|
||||
it('computes correct chunks if files with the same name is in folders with the same name', () => {
|
||||
const result = getFilePathParts(
|
||||
'/src/utils/Ownable.sol',
|
||||
[
|
||||
'/src/utils/EIP712.sol'.split('/'),
|
||||
'/lib/_openzeppelin/contracts/src/utils/Ownable.sol'.split('/'),
|
||||
],
|
||||
);
|
||||
|
||||
expect(result).toEqual([ 'Ownable.sol', './src/utils' ]);
|
||||
});
|
||||
|
||||
it('computes correct chunks if file is in root directory', () => {
|
||||
const result = getFilePathParts(
|
||||
'/Ownable.sol',
|
||||
[
|
||||
'/src/utils/EIP712.sol'.split('/'),
|
||||
'/lib/_openzeppelin/contracts/src/utils/Ownable.sol'.split('/'),
|
||||
],
|
||||
);
|
||||
|
||||
expect(result).toEqual([ 'Ownable.sol', './' ]);
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
export default function getFilePathParts(path: string, tabsPathChunks: Array<Array<string>>) {
|
||||
const chunks = path.split('/');
|
||||
const fileName = chunks[chunks.length - 1];
|
||||
const folderName = getFolderName(chunks, tabsPathChunks);
|
||||
|
||||
return [ fileName, folderName ];
|
||||
}
|
||||
|
||||
function getFolderName(chunks: Array<string>, tabsPathChunks: Array<Array<string>>): string | undefined {
|
||||
const fileName = chunks[chunks.length - 1];
|
||||
const otherTabsPathChunks = tabsPathChunks.filter((item) => item.join('/') !== chunks.join('/'));
|
||||
const tabsWithSameFileName = otherTabsPathChunks.filter((tabChunks) => tabChunks[tabChunks.length - 1] === fileName);
|
||||
|
||||
if (tabsWithSameFileName.length === 0 || chunks.length <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (chunks.length === 2) {
|
||||
return './' + chunks[chunks.length - 2];
|
||||
}
|
||||
|
||||
let result = '/' + chunks[chunks.length - 2];
|
||||
|
||||
for (let index = 3; index <= chunks.length; index++) {
|
||||
const element = chunks[chunks.length - index];
|
||||
|
||||
if (element === '') {
|
||||
result = '.' + result;
|
||||
}
|
||||
|
||||
const subFolderNames = tabsWithSameFileName.map((tab) => tab[tab.length - index]);
|
||||
if (subFolderNames.includes(element)) {
|
||||
result = '/' + element + result;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
Reference in New Issue
Block a user