diff --git a/src/filehandle/components/AddFileInput.tsx b/src/filehandle/components/AddFileInput.tsx new file mode 100644 index 0000000..7e8cc66 --- /dev/null +++ b/src/filehandle/components/AddFileInput.tsx @@ -0,0 +1,120 @@ +import { useState } from 'react'; + +import type { InputProps } from 'antd'; +import { Input, Spin } from 'antd'; + +import { error, uuid, validateFileName } from '@/utils'; + +import { createDirectory, createFile, FileType } from '@/filehandle/utils/fileManager'; + +import { ExtendFileInfo } from './FileSideMenu'; + +function AddFileInput({ + file, + names, + replace, + removeInput +}: { + file: ExtendFileInfo; + names: string[]; + replace(info: ExtendFileInfo, id: string): any; + removeInput(id: string): any; +}) { + const DEFAULT_NAME = 'default'; + + const isAddFile = file.type === FileType.FILE; + + const [inputStatus, setInputStatus] = useState<{ + name: string; + status: InputProps['status']; + }>({ name: '', status: '' }); + + const [loading, setLoading] = useState(false); + + const handleInputChange = (e: React.ChangeEvent) => { + const value = e.target.value.trim(); + + if (!validateFileName(value) || names.some((n) => n === value)) { + return setInputStatus({ name: value, status: 'error' }); + } + + setInputStatus({ name: value, status: '' }); + }; + + const handleKeyUp = (e: React.KeyboardEvent) => { + if (e.key.toLocaleLowerCase() !== 'enter') { + return void 0; + } + + handleAdd(); + }; + + const handleAdd = async () => { + const reg = new RegExp(`(?<=default)\\d*(?=${isAddFile ? '.md' : ''})`); + + let name = inputStatus.name; + if (inputStatus.name === '' || inputStatus.status !== '') { + const max = names + .map((n) => n.match(reg)) + .filter(Boolean) + .map(Number) + .sort((a, b) => a - b) + .pop(); + + if (typeof max === 'number') name = DEFAULT_NAME + (max + 1); + else name = DEFAULT_NAME; + } + + if (isAddFile) name += '.md'; + + if (file.handle.kind === 'directory') { + try { + setLoading(true); + + const handle = await (isAddFile ? createFile : createDirectory)(file.handle, name); + + replace( + { + id: uuid(), + name, + type: file.type, + handle, + icon: '', + parent: file.parent + }, + file.id + ); + } catch (err) { + error((err as Error).message); + removeInput(file.id); + } finally { + setLoading(false); + } + } else { + removeInput(file.id); + } + }; + + return ( + + { + e.stopPropagation(); + e.preventDefault(); + }} + onKeyUp={handleKeyUp} + onBlur={handleAdd} + /> + + ); +} + +export default AddFileInput; diff --git a/src/filehandle/components/FileContent.tsx b/src/filehandle/components/FileContent.tsx index 2d1e91d..fcd9dba 100644 --- a/src/filehandle/components/FileContent.tsx +++ b/src/filehandle/components/FileContent.tsx @@ -19,17 +19,17 @@ import { FileType } from '../utils/fileManager'; function MountWebdavModal(props: { open: boolean; close(): void }) { const { open, close } = props; - const [form] = Form.useForm(); - const { children } = useContext(FileSystemContext); const { addWebdav } = useUserStore(); + const [form] = Form.useForm(); + const handleOk = () => { form .validateFields() .then(() => { - const webdav: WebdavInfo = form.getFieldsValue(); + const webdav = form.getFieldsValue(); if (children.some((w) => w.name === webdav.name.trim())) { alert({ diff --git a/src/filehandle/md_editor/component/MDSidebar.tsx b/src/filehandle/components/FileSideMenu.tsx similarity index 73% rename from src/filehandle/md_editor/component/MDSidebar.tsx rename to src/filehandle/components/FileSideMenu.tsx index 5f12f21..6b4e235 100644 --- a/src/filehandle/md_editor/component/MDSidebar.tsx +++ b/src/filehandle/components/FileSideMenu.tsx @@ -1,35 +1,20 @@ import { useEffect, useMemo, useRef, useState } from 'react'; -import type { InputProps } from 'antd'; -import { Input, Layout, Spin } from 'antd'; +import { Layout } from 'antd'; import classNames from 'classnames'; -import { error, uuid, validateFileName } from '@/utils'; - -import { isFileHandle } from '@/filehandle/utils/checkFileType'; -import type { DH, FH, FileInfo } from '@/filehandle/utils/fileManager'; -import { - createDirectory, - createFile, - FileType, - getChildren, - remove -} from '@/filehandle/utils/fileManager'; +import { error, uuid } from '@/utils'; import { ContextMenu, Icon } from '@/components'; -import styles from './styles/MDSidebar.module.less'; +import AddFileInput from './AddFileInput'; +import styles from './styles/FileSideMenu.module.less'; +import { isFileHandle } from '../utils/checkFileType'; +import type { DH, FH, FileInfo } from '../utils/fileManager'; +import { FileType, getChildren, remove } from '../utils/fileManager'; -interface ISidebarProps { - handle: DH; - changed: boolean; - update(): void; - onSelect(handle: FH): void; - onRemove(handle: DH | FH): void; -} - -interface ExtendFileInfo extends FileInfo { +export interface ExtendFileInfo extends FileInfo { id: string; parent?: ExtendFileInfo; children?: ExtendFileInfo[]; @@ -66,7 +51,21 @@ function wrapperFileItem(parent: ExtendFileInfo, file: FileInfo): ExtendFileInfo return { ...file, id: uuid(), parent }; } -async function getDeepChildren(handle: DH, parent?: ExtendFileInfo): Promise { +function isSelect(file: FileInfo, exts?: string[]) { + if (!exts || exts.length === 0) { + return true; + } + + if (!file.ext) return false; + + return exts.includes(file.ext); +} + +async function getDeepChildren( + handle: DH, + parent?: ExtendFileInfo, + exts?: string[] +): Promise { parent = parent || { id: uuid(), name: handle.name, @@ -79,11 +78,11 @@ async function getDeepChildren(handle: DH, parent?: ExtendFileInfo): Promise value.type === FileType.DIRECTORY || value.ext === '.md') + .filter((value) => value.type === FileType.DIRECTORY || isSelect(value, exts)) .map(async (child) => { const newChild = wrapperFileItem(parent, child); if (child.handle.kind === 'directory') { - newChild.children = await getDeepChildren(child.handle, newChild); + newChild.children = await getDeepChildren(child.handle, newChild, exts); } return newChild; @@ -95,113 +94,15 @@ async function getDeepChildren(handle: DH, parent?: ExtendFileInfo): Promise({ name: '', status: '' }); - - const [loading, setLoading] = useState(false); - - const handleInputChange = (e: React.ChangeEvent) => { - const value = e.target.value.trim(); - - if (!validateFileName(value) || names.some((n) => n === value)) { - return setInputStatus({ name: value, status: 'error' }); - } - - setInputStatus({ name: value, status: '' }); - }; - - const handleKeyUp = (e: React.KeyboardEvent) => { - if (e.key.toLocaleLowerCase() !== 'enter') { - return void 0; - } - - handleAdd(); - }; - - const handleAdd = async () => { - const reg = new RegExp(`(?<=default)\\d*(?=${isAddFile ? '.md' : ''})`); - - let name = inputStatus.name; - if (inputStatus.name === '' || inputStatus.status !== '') { - const max = names - .map((n) => n.match(reg)) - .filter(Boolean) - .map(Number) - .sort((a, b) => a - b) - .pop(); - - if (typeof max === 'number') name = DEFAULT_NAME + (max + 1); - else name = DEFAULT_NAME; - } - - if (isAddFile) name += '.md'; - - if (file.handle.kind === 'directory') { - try { - setLoading(true); - - const handle = await (isAddFile ? createFile : createDirectory)(file.handle, name); - - replace( - { - id: uuid(), - name, - type: file.type, - handle, - icon: '', - parent: file.parent - }, - file.id - ); - } catch (err) { - error((err as Error).message); - removeInput(file.id); - } finally { - setLoading(false); - } - } else { - removeInput(file.id); - } - }; +const insetFile = (children: ExtendFileInfo[], child: ExtendFileInfo) => { + if (child.type === FileType.DIRECTORY) { + const i = children.findIndex((c) => c.type !== FileType.DIRECTORY); - return ( - - { - e.stopPropagation(); - e.preventDefault(); - }} - onKeyUp={handleKeyUp} - onBlur={handleAdd} - /> - - ); -} + i === -1 ? children.push(child) : children.splice(i, 0, child); + } else { + children.push(child); + } +}; function Menu({ activeId, @@ -263,6 +164,7 @@ function Menu({ ) : ( e.preventDefault()} title={item.name} onContextMenu={(e) => { @@ -313,18 +215,17 @@ function Menu({ ); } -const insetFile = (children: ExtendFileInfo[], child: ExtendFileInfo) => { - if (child.type === FileType.DIRECTORY) { - const i = children.findIndex((c) => c.type !== FileType.DIRECTORY); - - i === -1 ? children.push(child) : children.splice(i, 0, child); - } else { - children.push(child); - } -}; +interface IFileSideMenuProps { + handle: DH; + changed: boolean; + exts?: string[]; + update(): void; + onSelect(handle: FH): void; + onRemove(handle: DH | FH): void; +} -export const Sidebar: React.FC> = (props) => { - const { handle, changed, update, onSelect, onRemove } = props; +const FileSideMenu: React.FC> = (props) => { + const { handle, changed, exts, update, onSelect, onRemove } = props; const [list, setList] = useState([]); const [activeId, setActiveId] = useState(''); @@ -340,7 +241,7 @@ export const Sidebar: React.FC> = (props) => { } queryingRef.current = true; - getDeepChildren(handle) + getDeepChildren(handle, void 0, exts) .then((list) => { setList(list); }) @@ -491,3 +392,5 @@ export const Sidebar: React.FC> = (props) => { ); }; + +export default FileSideMenu; diff --git a/src/filehandle/md_editor/component/styles/MDSidebar.module.less b/src/filehandle/components/styles/FileSideMenu.module.less similarity index 100% rename from src/filehandle/md_editor/component/styles/MDSidebar.module.less rename to src/filehandle/components/styles/FileSideMenu.module.less diff --git a/src/filehandle/md_editor/component/MDContent.tsx b/src/filehandle/md_editor/component/MDContent.tsx index 359c0b6..8e33dcd 100644 --- a/src/filehandle/md_editor/component/MDContent.tsx +++ b/src/filehandle/md_editor/component/MDContent.tsx @@ -2,13 +2,13 @@ import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 're import { addGlobalListener, confirm, error } from '@/utils'; +import FileSideMenu from '@/filehandle/components/FileSideMenu'; import { isDirectoryHandle, isFileHandle } from '@/filehandle/utils/checkFileType'; import type { DH, FH } from '@/filehandle/utils/fileManager'; import { writeFile } from '@/filehandle/utils/fileManager'; import type { IMDEditorExpose } from './MDEditor'; import MDEditor from './MDEditor'; -import { Sidebar } from './MDSidebar'; import styles from './styles/MDContent.module.less'; interface IMDContentProps { @@ -129,9 +129,10 @@ export const MDContent = forwardRef( return (
{isDirectoryHandle(handle) ? ( -