Gitako/src/components/FileExplorer/index.tsx
2022-05-29 17:05:04 +08:00

143 lines
5.4 KiB
TypeScript

import { Label, Text } from '@primer/react'
import { LoadingIndicator } from 'components/LoadingIndicator'
import { SearchBar } from 'components/SearchBar'
import * as React from 'react'
import { cx } from 'utils/cx'
import { run } from 'utils/general'
import { useLoadedContext } from 'utils/hooks/useLoadedContext'
import { VisibleNodes } from 'utils/VisibleNodesGenerator'
import { SideBarStateContext } from '../../containers/SideBarState'
import { SizeObserver } from '../SizeObserver'
import { useFocusFileExplorerOnFirstRender } from './hooks/useFocusFileExplorerOnFirstRender'
import { useGetCurrentPath } from './hooks/useGetCurrentPath'
import { useHandleKeyDown } from './hooks/useHandleKeyDown'
import { useNodeRenderContext } from './hooks/useNodeRenderContext'
import {
NodeRenderer,
useNodeRenderers,
useRenderFileCommentAmounts,
useRenderFileStatus,
useRenderFindInFolderButton,
useRenderGoToButton
} from './hooks/useNodeRenderers'
import { useHandleNodeClick } from './hooks/useOnNodeClick'
import { useOnSearch } from './hooks/useOnSearch'
import { useVisibleNodesGeneratorMethods } from './hooks/useOnVisibleNodesGeneratorReady'
import { useRenderLabelText } from './hooks/useRenderLabelText'
import { useVisibleNodesGenerator } from './hooks/useSetupTree'
import { ListView } from './ListView'
import { useHandleNodeFocus } from './useHandleNodeFocus'
import { useVisibleNodes } from './useVisibleNodes'
export type NodeRendererContext = {
onNodeClick: (event: React.MouseEvent<HTMLElement, MouseEvent>, node: TreeNode) => void
onNodeFocus: (event: React.FocusEvent<HTMLElement, Element>, node: TreeNode) => void
renderLabelText: NodeRenderer
renderActions: NodeRenderer | undefined
visibleNodes: VisibleNodes
}
type Props = {
metaData: MetaData
freeze: boolean
}
export function FileExplorer({ freeze, metaData }: Props) {
const visibleNodesGenerator = useVisibleNodesGenerator(metaData)
const visibleNodes = useVisibleNodes(visibleNodesGenerator)
const [searchKey, updateSearchKey] = React.useState('')
const searched = !!searchKey
const onSearch = useOnSearch(updateSearchKey, visibleNodesGenerator)
const getCurrentPath = useGetCurrentPath(metaData)
const methods = useVisibleNodesGeneratorMethods(
visibleNodesGenerator,
getCurrentPath,
updateSearchKey,
)
const { expandTo, goTo, focusNode } = methods
const handleNodeClick = useHandleNodeClick(methods)
const handleNodeFocus = useHandleNodeFocus(methods)
const handleKeyDown = useHandleKeyDown(visibleNodes, methods, searched)
const handleFocusSearchBar = () => focusNode(null)
const renderActions = useNodeRenderers([
useRenderGoToButton(searched, goTo),
useRenderFindInFolderButton(onSearch),
useRenderFileCommentAmounts(),
useRenderFileStatus(),
])
const renderLabelText = useRenderLabelText(searchKey)
const nodeRendererContext = useNodeRenderContext(
visibleNodes,
handleNodeClick,
handleNodeFocus,
renderActions,
renderLabelText,
)
const state = useLoadedContext(SideBarStateContext).value
useFocusFileExplorerOnFirstRender()
return (
<div className={cx(`file-explorer`, { freeze })} tabIndex={-1} onKeyDown={handleKeyDown}>
{run(() => {
switch (state) {
case 'tree-loading':
return <LoadingIndicator text={'Fetching File List...'} />
case 'tree-rendering':
return <LoadingIndicator text={'Rendering File List...'} />
case 'tree-rendered':
return (
visibleNodes &&
nodeRendererContext && (
<>
{visibleNodesGenerator?.defer && (
<div className={'status'}>
<Label
title="This repository is large. Gitako has switched to Lazy Mode to improve performance. Folders will be loaded on demand."
className={'lazy-mode'}
variant="attention"
>
Lazy Mode is ON
</Label>
</div>
)}
<SearchBar value={searchKey} onSearch={onSearch} onFocus={handleFocusSearchBar} />
{searched && visibleNodes.nodes.length === 0 && (
<>
<Text marginTop={6} textAlign="center" color="text.gray">
No results found.
</Text>
{visibleNodesGenerator?.defer && (
<Text textAlign="center" color="gray.4" fontSize="12px">
Search results are limited to loaded folders in Lazy Mode.
</Text>
)}
</>
)}
{visibleNodes.nodes.length > 0 && (
<SizeObserver<HTMLDivElement>>
{({ width = 0, height = 0 }, ref) => (
<div className={'files'} ref={ref}>
<ListView
height={height}
width={width}
nodeRendererContext={nodeRendererContext}
expandTo={expandTo}
metaData={metaData}
/>
</div>
)}
</SizeObserver>
)}
</>
)
)
}
})}
</div>
)
}