一个快速选择文件夹树的组件
开发
文件树
explorer/src/components/readdir-tree/index.tsx
explorer/src/components/select-path-input/index.tsx
文件路径:explorer/src/components/select-path-input/index.tsx
封装一个泡泡弹窗,弹窗内容为文件树组件
'use client'
import React, { useState } from 'react'
import { Card, Popover } from 'antd'
import styled from 'styled-components'
import { OnSelectType, Tree } from '@/components/readdir-tree'
const PopoverItem = styled(Card)`
overflow-y: scroll;
overscroll-behavior: contain;
max-height: 30vh;
`
const SelectPathInput: React.FC<React.PropsWithChildren & { onSelect: OnSelectType; highlight_path?: string }> = ({
children,
onSelect,
highlight_path,
}) => {
const [open, setOpen] = useState(false)
const handleOpenChange = (new_open: boolean) => {
setOpen(new_open)
}
return (
<Popover
content={
<PopoverItem>
<Tree onSelect={onSelect} highlight_path={highlight_path} />
</PopoverItem>
}
placement="bottomLeft"
title="目录"
trigger="click"
open={open}
onOpenChange={handleOpenChange}
arrow={false}
>
<>{children}</>
</Popover>
)
}
export default SelectPathInput
文件路径:explorer/src/components/readdir-tree/index.tsx
使用 ul、li、文件夹 icon 的文件树组件
- ul 被装载时通过传入的 parent\_path 指定的目录下的文件夹列表。useReaddirRequest 方法。并使用 map 渲染 li 组件。
- li 组件内置一个 open 状态,当点击 li 时,将 open 设置为真,组合 parent\_path.concat(item.name) 传递给 ul 组件。此时新的 ul 组件被装载,触发 “1” 流程。
- 接受一个 highlight\_path 的 props。使用 new RegExp(\`^${encodeURIComponent(path.join('/'))}\`).test(encodeURIComponent(highlight\_path)) 正则匹配高亮目录,加强目录提示。具体效果可看最后的“效果”图片。
通过上面的流程简单实现了文件夹树组件
'use client'
import React, { useContext, useState } from 'react'
import { Button, Space } from 'antd'
import { ReaddirItemType, ReaddirListType } from '@/explorer-manager/src/type'
import styled from 'styled-components'
import { isEmpty } from 'lodash'
import { FolderOpenOutlined, FolderOutlined, LoadingOutlined, MinusOutlined } from '@ant-design/icons'
import { useRequest } from 'ahooks'
import axios from 'axios'
export const LiStyle = styled.li``
export const UlStyle = styled.ul`
padding-left: 20px;
list-style: none;
`
export type OnSelectType = (path: string[]) => void
const SelectPathInputContext = React.createContext<{ onSelect?: OnSelectType; highlight_path?: string }>(null!)
const useReaddirRequest = (
{ manual, init_path }: { manual: boolean; init_path: string } = { manual: false, init_path: '' },
) => {
return useRequest(
(path: string = init_path) =>
axios
.get<{ readdir: ReaddirListType }>('/path/api/readdir', {
params: { path: path, only_dir: 1 },
})
.then(({ data: { readdir } }) => {
return readdir
}),
{ manual, cacheKey: init_path ? init_path : undefined, staleTime: 5000 },
)
}
const EmptyItem: React.FC<React.PropsWithChildren> = ({ children }) => {
return (
<UlStyle>
<li>{children}</li>
</UlStyle>
)
}
const Li: React.FC<{ item: ReaddirItemType; parent_path: string[] }> = ({ item, parent_path }) => {
const [open, changeOpen] = useState(false)
const path = parent_path.concat(item.name)
const ctx = useContext(SelectPathInputContext)
const { highlight_path = '' } = ctx
const is_highlight =
!!highlight_path && new RegExp(`^${encodeURIComponent(path.join('/'))}`).test(encodeURIComponent(highlight_path))
const btn_tye = is_highlight ? 'primary' : 'text'
return (
<LiStyle data-h={highlight_path} data-p={path.join('/')}>
<Space.Compact
onClick={() => {
changeOpen(!open)
}}
>
{open ? (
<Button type={btn_tye} icon={<FolderOpenOutlined />} />
) : (
<Button type={btn_tye} icon={<FolderOutlined />} />
)}
<Button type={btn_tye} onClick={() => ctx.onSelect?.(path)}>
{item.name}
</Button>
</Space.Compact>
{open && <Ul parent_path={path} />}
</LiStyle>
)
}
const Ul: React.FC<{ parent_path: string[] }> = ({ parent_path = [] }) => {
const { data: dir_list, loading } = useReaddirRequest({ manual: false, init_path: parent_path.join('/') })
return !dir_list && loading ? (
<EmptyItem>
<LoadingOutlined />
</EmptyItem>
) : isEmpty(dir_list) ? (
<EmptyItem>
<MinusOutlined />
</EmptyItem>
) : (
<UlStyle>
{dir_list?.map((item) => {
return <Li key={item.name} item={item} parent_path={parent_path} />
})}
</UlStyle>
)
}
export const Tree: React.FC<{ onSelect?: OnSelectType; highlight_path?: string }> = ({ onSelect, highlight_path }) => {
return (
<SelectPathInputContext.Provider value={{ onSelect, highlight_path: highlight_path?.replace(/^\//, '') }}>
<Ul parent_path={[]} />
</SelectPathInputContext.Provider>
)
}
可用于类似 win 系统资源管理器左边的菜单树功能,提供快速跳转目录。或者 input 快速选择输入所需要的目录地址。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。