import { Box } from '@chakra-ui/react'; import { throttle } from 'es-toolkit'; import type * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; import React from 'react'; import type { File, Monaco } from './types'; import type { TabsTriggerProps } from 'toolkit/chakra/tabs'; import { TabsContent, TabsList, TabsRoot, TabsTrigger } from 'toolkit/chakra/tabs'; import { shift, cmd } from 'toolkit/utils/htmlEntities'; import CodeEditorFileExplorer from './CodeEditorFileExplorer'; import CodeEditorSearch from './CodeEditorSearch'; import useThemeColors from './utils/useThemeColors'; interface Props { monaco: Monaco | undefined; editor: monaco.editor.IStandaloneCodeEditor | undefined; data: Array; onFileSelect: (index: number, lineNumber?: number) => void; selectedFile: string; mainFile?: string; } export const CONTAINER_WIDTH = 250; const CodeEditorSideBar = ({ onFileSelect, data, monaco, editor, selectedFile, mainFile }: Props) => { const [ isStuck, setIsStuck ] = React.useState(false); const [ isDrawerOpen, setIsDrawerOpen ] = React.useState(false); const [ activeTab, setActiveTab ] = React.useState('explorer'); const [ searchValue, setSearchValue ] = React.useState(''); const [ actionBarRenderer, setActionBarRenderer ] = React.useState<() => React.JSX.Element>(); const themeColors = useThemeColors(); const tabProps: Partial = { fontFamily: 'heading', textTransform: 'uppercase', fontSize: '11px', lineHeight: '35px', fontWeight: 500, color: themeColors['tab.inactiveForeground'], _selected: { color: themeColors['tab.activeForeground'], }, px: 0, letterSpacing: 0.3, }; const handleScrollThrottled = React.useRef(throttle((event: React.SyntheticEvent) => { setIsStuck((event.target as HTMLDivElement).scrollTop > 0); }, 100)); const handleFileSelect = React.useCallback((index: number, lineNumber?: number) => { isDrawerOpen && setIsDrawerOpen(false); onFileSelect(index, lineNumber); }, [ isDrawerOpen, onFileSelect, setIsDrawerOpen ]); const handleSideBarButtonClick = React.useCallback(() => { setIsDrawerOpen((prev) => !prev); }, []); const handleTabChange = React.useCallback(({ value }: { value: string }) => { setActiveTab(value); }, []); React.useEffect(() => { if (editor && monaco) { editor.addAction({ id: 'file-explorer', label: 'Show File Explorer', keybindings: [ monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KeyE, ], contextMenuGroupId: 'navigation', contextMenuOrder: 1.5, run: function() { setActiveTab('explorer'); }, }); editor.addAction({ id: 'search-in-files', label: 'Show Search in Files', keybindings: [ monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KeyF, ], contextMenuGroupId: 'navigation', contextMenuOrder: 1.6, run: function(editor) { setActiveTab('search'); const selection = editor.getSelection(); const selectedValue = selection ? editor.getModel()?.getValueInRange(selection) : ''; setSearchValue(selectedValue || ''); }, }); } }, [ editor, monaco ]); return ( <> Explorer Search { actionBarRenderer?.() } ); }; export default React.memo(CodeEditorSideBar);