From c15758936174d473e7233af7f8ceafddac987659 Mon Sep 17 00:00:00 2001 From: BUPTlhuanyu Date: Thu, 15 Jul 2021 00:00:56 +0800 Subject: [PATCH 1/3] style(code cr): code cr --- packages/views/src/electron/menu/index.ts | 1 - .../editor/components/sider/header/index.scss | 59 +++++ .../editor/components/sider/header/index.tsx | 99 ++++++++ .../pages/editor/components/sider/index.scss | 8 + .../pages/editor/components/sider/index.tsx | 221 ++---------------- .../pages/editor/components/sider/utils.ts | 145 ++++++++++++ 6 files changed, 327 insertions(+), 206 deletions(-) create mode 100644 packages/views/src/pages/editor/components/sider/header/index.scss create mode 100644 packages/views/src/pages/editor/components/sider/header/index.tsx create mode 100644 packages/views/src/pages/editor/components/sider/utils.ts diff --git a/packages/views/src/electron/menu/index.ts b/packages/views/src/electron/menu/index.ts index f01f361..4d371ec 100644 --- a/packages/views/src/electron/menu/index.ts +++ b/packages/views/src/electron/menu/index.ts @@ -33,7 +33,6 @@ export function registerContextMenu() { export function registerTopMenuListener() { pandora.ipcRenderer.on(`pandora:${CREATE_FILE}`, () => { - console.log('pandora:pandora:'); fileEvent.emit(FS_CREATE_FILE); }); pandora.ipcRenderer.on(`pandora:${CREATE_DIR}`, () => { diff --git a/packages/views/src/pages/editor/components/sider/header/index.scss b/packages/views/src/pages/editor/components/sider/header/index.scss new file mode 100644 index 0000000..283dcd0 --- /dev/null +++ b/packages/views/src/pages/editor/components/sider/header/index.scss @@ -0,0 +1,59 @@ +.sider-panel-header { + line-height: 30px; + height: 30px; + background-color: #f3f3f7; + display: flex; + justify-content: space-between; + align-items: center; + box-shadow: rgba(17, 17, 26, 0.1) 0px 2px 4px, rgba(17, 17, 26, 0.05) 0px 4px 8px; + &-icon { + display: flex; + cursor: pointer; + } + &-back { + &:hover { + background: #e1e1e1; + border-radius: 6px; + } + } + .ant-input-affix-wrapper { + border: none; + width: 0%; + animation: widthSpread .3s ease-in forwards; + } + .ant-input-affix-wrapper :focus{ + border-color: #FFB531; + box-shadow: none; + } + .ant-input-affix-wrapper-focused { + // border-color: #FFB531; + border: none; + box-shadow: none; + } + &-input { + height: 20px; + background: transparent; + padding: 0; + margin-right: 4px; + margin-left: 4px; + border-radius: 0; + border-top: 0; + border-left: 0; + border-right: 0; + &:focus, &:hover { + border-color: #FFB531; + box-shadow: none; + } + &-selected { + background: #e1e1e1; + border-radius: 6px; + } + .ant-input { + font-size: 12px; + background: transparent; + } + .ant-input-suffix { + display: flex; + } + } +} diff --git a/packages/views/src/pages/editor/components/sider/header/index.tsx b/packages/views/src/pages/editor/components/sider/header/index.tsx new file mode 100644 index 0000000..8cc30e2 --- /dev/null +++ b/packages/views/src/pages/editor/components/sider/header/index.tsx @@ -0,0 +1,99 @@ +// 非受控,减少外层代码量 +import * as React from 'react'; +import './index.scss'; +import getClassname from 'views/src/utils/classMaker'; + +import Icon from 'views/src/components/icon'; +import {Input} from 'antd'; + +interface ISiderProps { + className?: string; + onBack?: React.MouseEventHandler; + onPressEnter?: (value: string, caseSensitive: boolean, wholeWord: boolean) => void; + onCaseSensitive?: React.MouseEventHandler; + onCheckWholeWord?: React.MouseEventHandler; + focused?: boolean; +} + +export default function Header(props: ISiderProps) { + const [caseSensitive, setCaseSensitive] = React.useState(false); + const [wholeWord, setWholeWord] = React.useState(false); + const inputRef = React.useRef(null); + + const [className] = React.useState(() => { + return getClassname([ + 'sider-panel-header', + props.className + ]); + }); + + const onCaseSensitive = React.useCallback(() => { + setCaseSensitive(!caseSensitive); + }, [caseSensitive, setCaseSensitive, props.onCaseSensitive]); + + const onCheckWholeWord = React.useCallback(() => { + setWholeWord(!wholeWord); + }, [wholeWord, setWholeWord, props.onCheckWholeWord]); + + const onPressEnter = React.useCallback( + e => { + if (!e || !e.target) { + return; + } + const value = e.target.value; + typeof props.onPressEnter === 'function' && props.onPressEnter(value, caseSensitive, wholeWord); + }, + [props.onPressEnter, caseSensitive, wholeWord] + ); + + React.useEffect(() => { + props.focused && inputRef.current && inputRef.current?.focus(); + }, [props.focused]); + + return ( +
+ {/* TODO: renderProp */} + + + + + + + + + + + + } + /> +
+ ); +}; + +function noop() {} +Header.defaultProps = { + className: '', + onBack: noop, + onPressEnter: noop, + onCaseSensitive: noop, + onCheckWholeWord: noop, + focused: noop +}; diff --git a/packages/views/src/pages/editor/components/sider/index.scss b/packages/views/src/pages/editor/components/sider/index.scss index 4b7a7a8..b70482d 100644 --- a/packages/views/src/pages/editor/components/sider/index.scss +++ b/packages/views/src/pages/editor/components/sider/index.scss @@ -148,6 +148,14 @@ border-top-width: 1px; border-top-style: solid; } + .sider-file-empty { + display: flex; + flex: 1; + height: 100%; + justify-content: center; + align-items: center; + color: #7a7a7a; + } .sider-file-folder { background: transparent; overflow-x: auto; diff --git a/packages/views/src/pages/editor/components/sider/index.tsx b/packages/views/src/pages/editor/components/sider/index.tsx index 446da9b..b09bc2d 100644 --- a/packages/views/src/pages/editor/components/sider/index.tsx +++ b/packages/views/src/pages/editor/components/sider/index.tsx @@ -1,168 +1,29 @@ -import * as React from 'react'; import './index.scss'; -import getClassname from 'views/src/utils/classMaker'; +import * as React from 'react'; +import produce from 'immer'; + import FileFolder, {ItreeData} from 'views/src/components/file-folder'; import SearchList, {ISearchResult} from 'views/src/components/search-list'; import TocList, {ITocItem} from 'views/src/components/toc-list'; - import Icon from 'views/src/components/icon'; +import Header from './header'; + import {pandora} from 'views/src/services/pandora'; import {revealFileInOs, moveFileToTrash} from 'views/src/services/messageCenter'; + import {FS_CREATE_FILE, FS_CREATE_DIR, FS_EDIT, FS_DELETE, fileEvent, FS_REVEAL} from 'views/src/utils/event'; -import {Input} from 'antd'; -import produce from 'immer'; +import {isFilePath} from 'views/src/utils/tools'; import {getMdOutline} from 'views/src/utils/markdown-helper'; +import getClassname from 'views/src/utils/classMaker'; +import {addToTreeData, deleteToTreeData, getRootPath, updateNodeData} from './utils'; import {FileContext} from 'views/src/pages/editor/store/sidbar'; import {EditorContext} from 'views/src/pages/editor/store/editor'; -import {isFilePath} from 'views/src/utils/tools'; - -function updateNodeData(nodeData: Record, newName: string) { - if (!nodeData || !nodeData.path || !newName) { - return; - } - const lastIndexPath = nodeData.path.lastIndexOf('/'); - const parentPath = nodeData.path.substring(0, lastIndexPath); - const newPath = `${parentPath}/${newName}`; - const lastIndexExt = newName.lastIndexOf('.'); - nodeData.extension = lastIndexExt > 0 ? newName.substring(newName.lastIndexOf('.')) : ''; - nodeData.key = newPath; - nodeData.path = newPath; - nodeData.title = newName; - nodeData.name = newName; - nodeData.exist = true; -} - interface ISiderProps { className: string; } -function getRootPath(path: string) { - if (typeof path !== 'string') { - return ''; - } - const lastIndex = path.lastIndexOf('/'); - let rootPath = path; - if (path.indexOf('.') > 0) { - rootPath = path.substring(0, lastIndex); - } - return rootPath; -} - -function createFile(path: string, index: number) { - return { - extension: '.md', - index: [], - isLeaf: true, - key: `${path}/Untitled${index}.md`, - name: `Untitled${index}.md`, - path: `${path}/Untitled${index}.md`, - size: 0, - title: `Untitled${index}.md`, - type: 'file', - exist: false - }; -} - -function createDir(path: string, index: number) { - return { - children: [], - exist: false, - size: 0, - name: `Untitled Folder${index}`, - title: `Untitled Folder${index}`, - key: `${path}/Untitled Folder${index}`, - path: `${path}/Untitled Folder${index}`, - type: 'directory' - }; -} - -function addToTreeData(tree: any, path: string, type: 'file' | 'directory') { - let addedNode: Record = {}; - let nextTree = produce(tree, (draftTree: any) => { - let item = null; - let root = draftTree[0]; - if (root) { - let stack: Record = [root]; - while (stack.length > 0) { - let node = stack.pop(); - if (node.type === 'directory' && Array.isArray(node.children) && node.path === path) { - let maxIndex = 1; - for (let i of node.children) { - let reg = type === 'file' ? /Untitled([^/]*)\.md$/ : /Untitled\sFolder([^/]*)$/; - const match = reg.exec(i.path); - if (match && match[1] && typeof +match[1] === 'number') { - let curIndex = +match[1]; - if (!Number.isNaN(curIndex) && typeof curIndex === 'number' && curIndex > maxIndex) { - maxIndex = curIndex; - } - } - } - maxIndex += 1; - item = type === 'file' ? createFile(path, maxIndex) : createDir(path, maxIndex); - node.children.push(item); - addedNode = item; - stack.length = 0; - return; - } - if (node.children && node.children.length > 0) { - let len = node.children.length; - for (let i = 0; i < len; i++) { - stack.push(node.children[i]); - } - } - } - } - }); - return { - nextTree, - node: addedNode - }; -} - -function deleteToTreeData(tree: any, path: string) { - let rootPath = getRootPath(path); - let selectedFile = ''; - let nextTree = produce(tree, (draftTree: any) => { - let root = draftTree[0]; - if (root) { - let stack: Record = [root]; - while (stack.length > 0) { - let node = stack.pop(); - if (Array.isArray(node.children) && node.path === rootPath) { - let len = node.children.length; - for (let i = 0; i < len; i++) { - if (node.children[i].path === path) { - if (node.children[i + 1]) { - selectedFile = node.children[i + 1].key; - } else if (node.children[i - 1]) { - selectedFile = node.children[i - 1].key; - } else { - selectedFile = node.key; - } - node.children.splice(i, 1); - break; - } - } - stack.length = 0; - return; - } - if (node.children && node.children.length > 0) { - let len = node.children.length; - for (let i = 0; i < len; i++) { - stack.push(node.children[i]); - } - } - } - } - }); - return { - nextTree, - selectedFile - }; -} - export default React.forwardRef(function Sider(props: ISiderProps, ref: any) { const contextRef = React.useRef(false); const [{editor}] = React.useContext(EditorContext); @@ -172,9 +33,6 @@ export default React.forwardRef(function Sider(props: ISiderProps, ref: any) { const [mouseEnter, setMouseEnter] = React.useState(false); const [showPanel, setShowPanel] = React.useState(false); - const [caseSensitive, setCaseSensitive] = React.useState(false); - const [wholeWord, setWholeWord] = React.useState(false); - const inputRef = React.useRef(null); const [searchResult, setSearchResult] = React.useState([]); const [showToc, setShowToc] = React.useState(false); @@ -283,28 +141,12 @@ export default React.forwardRef(function Sider(props: ISiderProps, ref: any) { } }, [setMouseEnter]); - const onCaseSensitive = React.useCallback(() => { - setCaseSensitive(!caseSensitive); - }, [caseSensitive, setCaseSensitive]); - - const onCheckWholeWord = React.useCallback(() => { - setWholeWord(!wholeWord); - }, [wholeWord, setWholeWord]); - const onShowPanel = React.useCallback(() => { setShowPanel(!showPanel); }, [showPanel, setShowPanel]); - React.useEffect(() => { - showPanel && inputRef.current && inputRef.current?.focus(); - }, [showPanel]); - const onStartSearch = React.useCallback( - e => { - if (!e || !e.target) { - return; - } - const value = e.target.value; + (value: string, caseSensitive: boolean, wholeWord: boolean) => { if (!value) { setSearchResult([]); } else if (treeData[0]) { @@ -323,7 +165,7 @@ export default React.forwardRef(function Sider(props: ISiderProps, ref: any) { } setShowPanel(true); }, - [setShowPanel, treeData, caseSensitive, wholeWord] + [setShowPanel, treeData] ); const onToc = React.useCallback(() => { @@ -518,7 +360,6 @@ export default React.forwardRef(function Sider(props: ISiderProps, ref: any) { ref={ref} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} - data-context="sider" >
@@ -536,39 +377,7 @@ export default React.forwardRef(function Sider(props: ISiderProps, ref: any) {
{showPanel && (
-
- - - - - - - - - - - - } - /> -
+
{searchResult.length > 0 ? )} - {treeData.length > 0 && ( + {treeData.length > 0 ? - )} + :
没有打开的文件夹
+ } {mouseEnter && !showToc && (
打开文件夹... diff --git a/packages/views/src/pages/editor/components/sider/utils.ts b/packages/views/src/pages/editor/components/sider/utils.ts new file mode 100644 index 0000000..911f0b6 --- /dev/null +++ b/packages/views/src/pages/editor/components/sider/utils.ts @@ -0,0 +1,145 @@ +/** + * @file + */ +import produce from 'immer'; + +export function getRootPath(path: string) { + if (typeof path !== 'string') { + return ''; + } + const lastIndex = path.lastIndexOf('/'); + let rootPath = path; + if (path.indexOf('.') > 0) { + rootPath = path.substring(0, lastIndex); + } + return rootPath; +} + +export function updateNodeData(nodeData: Record, newName: string) { + if (!nodeData || !nodeData.path || !newName) { + return; + } + const lastIndexPath = nodeData.path.lastIndexOf('/'); + const parentPath = nodeData.path.substring(0, lastIndexPath); + const newPath = `${parentPath}/${newName}`; + const lastIndexExt = newName.lastIndexOf('.'); + nodeData.extension = lastIndexExt > 0 ? newName.substring(newName.lastIndexOf('.')) : ''; + nodeData.key = newPath; + nodeData.path = newPath; + nodeData.title = newName; + nodeData.name = newName; + nodeData.exist = true; +} + +function createFile(path: string, index: number) { + return { + extension: '.md', + index: [], + isLeaf: true, + key: `${path}/Untitled${index}.md`, + name: `Untitled${index}.md`, + path: `${path}/Untitled${index}.md`, + size: 0, + title: `Untitled${index}.md`, + type: 'file', + exist: false + }; +} + +function createDir(path: string, index: number) { + return { + children: [], + exist: false, + size: 0, + name: `Untitled Folder${index}`, + title: `Untitled Folder${index}`, + key: `${path}/Untitled Folder${index}`, + path: `${path}/Untitled Folder${index}`, + type: 'directory' + }; +} + +export function addToTreeData(tree: any, path: string, type: 'file' | 'directory') { + let addedNode: Record = {}; + let nextTree = produce(tree, (draftTree: any) => { + let item = null; + let root = draftTree[0]; + if (root) { + let stack: Record = [root]; + while (stack.length > 0) { + let node = stack.pop(); + if (node.type === 'directory' && Array.isArray(node.children) && node.path === path) { + let maxIndex = 1; + for (let i of node.children) { + let reg = type === 'file' ? /Untitled([^/]*)\.md$/ : /Untitled\sFolder([^/]*)$/; + const match = reg.exec(i.path); + if (match && match[1] && typeof +match[1] === 'number') { + let curIndex = +match[1]; + if (!Number.isNaN(curIndex) && typeof curIndex === 'number' && curIndex > maxIndex) { + maxIndex = curIndex; + } + } + } + maxIndex += 1; + item = type === 'file' ? createFile(path, maxIndex) : createDir(path, maxIndex); + node.children.push(item); + addedNode = item; + stack.length = 0; + return; + } + if (node.children && node.children.length > 0) { + let len = node.children.length; + for (let i = 0; i < len; i++) { + stack.push(node.children[i]); + } + } + } + } + }); + return { + nextTree, + node: addedNode + }; +} + +export function deleteToTreeData(tree: any, path: string) { + let rootPath = getRootPath(path); + let selectedFile = ''; + let nextTree = produce(tree, (draftTree: any) => { + let root = draftTree[0]; + if (root) { + let stack: Record = [root]; + while (stack.length > 0) { + let node = stack.pop(); + if (Array.isArray(node.children) && node.path === rootPath) { + let len = node.children.length; + for (let i = 0; i < len; i++) { + if (node.children[i].path === path) { + if (node.children[i + 1]) { + selectedFile = node.children[i + 1].key; + } else if (node.children[i - 1]) { + selectedFile = node.children[i - 1].key; + } else { + selectedFile = node.key; + } + node.children.splice(i, 1); + break; + } + } + stack.length = 0; + return; + } + if (node.children && node.children.length > 0) { + let len = node.children.length; + for (let i = 0; i < len; i++) { + stack.push(node.children[i]); + } + } + } + } + }); + return { + nextTree, + selectedFile + }; +} From ba2d6645b4f7ea76186188a92b486884ce637bd3 Mon Sep 17 00:00:00 2001 From: BUPTlhuanyu Date: Thu, 15 Jul 2021 23:39:25 +0800 Subject: [PATCH 2/3] =?UTF-8?q?style(=E6=A2=B3=E7=90=86=E4=BB=A3=E7=A0=81)?= =?UTF-8?q?:=20=E6=A2=B3=E7=90=86=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/useCodemirror/code.ts | 20 ++ .../pages/editor/components/sider/index.tsx | 255 ++++++++---------- 2 files changed, 137 insertions(+), 138 deletions(-) diff --git a/packages/views/src/components/useCodemirror/code.ts b/packages/views/src/components/useCodemirror/code.ts index 607ef28..2b23575 100644 --- a/packages/views/src/components/useCodemirror/code.ts +++ b/packages/views/src/components/useCodemirror/code.ts @@ -119,4 +119,24 @@ export function createCodemirror(ele: any) { }); } +export const scrollToLine = (editor: codemirror.Editor, line: number) => { + if (!editor || !editor.getDoc) { + return; + } + // @ts-ignore + editor.scrollTo(0, editor.charCoords({line}, 'local').top - editor.charCoords({line}, 'line').top); + const textMarker = editor.getDoc().markText( + // @ts-ignore + {line: line - 1}, + {line}, + { + css: 'background-color: #FFFAAA', + atomic: true + } + ); + setTimeout(() => { + textMarker.clear(); + }, 500); +}; + export default codemirror; diff --git a/packages/views/src/pages/editor/components/sider/index.tsx b/packages/views/src/pages/editor/components/sider/index.tsx index b09bc2d..072b87f 100644 --- a/packages/views/src/pages/editor/components/sider/index.tsx +++ b/packages/views/src/pages/editor/components/sider/index.tsx @@ -20,52 +20,18 @@ import {addToTreeData, deleteToTreeData, getRootPath, updateNodeData} from './ut import {FileContext} from 'views/src/pages/editor/store/sidbar'; import {EditorContext} from 'views/src/pages/editor/store/editor'; +import {scrollToLine} from 'views/src/components/useCodemirror/code'; + interface ISiderProps { className: string; } export default React.forwardRef(function Sider(props: ISiderProps, ref: any) { - const contextRef = React.useRef(false); - const [{editor}] = React.useContext(EditorContext); + /* -------------------------------------------------------------------------- */ + /* store */ + /* -------------------------------------------------------------------------- */ const [{selectedFilePath}, dispatch] = React.useContext(FileContext); - const [renameKey, setRenameKey] = React.useState(''); - const [treeData, setTreeData] = React.useState([]); - const [mouseEnter, setMouseEnter] = React.useState(false); - - const [showPanel, setShowPanel] = React.useState(false); - const [searchResult, setSearchResult] = React.useState([]); - - const [showToc, setShowToc] = React.useState(false); - const [tocList, setTocList] = React.useState([]); - - // TODO: 放到 useCodeMirror - const scrollEditor = React.useCallback( - (line: number) => { - // TODO: 封装 - // 1. 去掉硬编码,34 是头部高度30 + padding高度4 - // 2. editor.charCoords({line}, 'line') 获取字符相对于行的top值 - // 3. editor.charCoords({line}).top 获取字符相对于page的top - editor.scrollTo(0, editor.charCoords({line}, 'local').top - editor.charCoords({line}, 'line').top); - const textMarker = editor.getDoc().markText( - {line: line - 1}, - {line}, - { - css: 'background-color: #FFFAAA', - atomic: true - } - ); - setTimeout(() => { - textMarker.clear(); - }, 500); - }, - [editor] - ); - - const onTocSelect = React.useCallback((item: ITocItem) => { - scrollEditor(item.line); - }, [editor]); - - const onSearchSelect = React.useCallback( + const changeSelectedFile = React.useCallback( filename => { dispatch({ type: 'selectedFile', @@ -74,52 +40,11 @@ export default React.forwardRef(function Sider(props: ISiderProps, ref: any) { }, [dispatch] ); + const [{editor}] = React.useContext(EditorContext); - const onLineSelect = React.useCallback( - (line: number, _, filename: string) => { - try { - if (filename !== selectedFilePath) { - dispatch({ - type: 'selectedFile', - payload: filename - }); - // TODO: 中间存在一个读取的操纵,因此这里会存在bug - setTimeout(() => { - scrollEditor(line); - }, 200); - } else { - scrollEditor(line); - } - } catch (e) {} - }, - [editor, selectedFilePath, dispatch] - ); - - const onSelect = React.useCallback( - (keys: Array, {node}: Record) => { - if (isFilePath(selectedFilePath)) { - const content = editor?.getDoc().getValue() || ''; - pandora && - pandora.ipcRenderer - .invoke('pandora:writeFile', selectedFilePath, content) - .then(() => { - // TODO: 编辑状态 - }) - .catch(err => { - console.log(err); - }); - } - dispatch({ - type: 'selectedFile', - payload: keys[0] || node.path - }); - if (keys[0] !== renameKey) { - setRenameKey(''); - } - }, - [dispatch, renameKey, editor, selectedFilePath] - ); - + /* -------------------------------------------------------------------------- */ + /* UI: class */ + /* -------------------------------------------------------------------------- */ const [className] = React.useState(() => { return getClassname({ 'pandora-sider-wrapper': true, @@ -127,6 +52,10 @@ export default React.forwardRef(function Sider(props: ISiderProps, ref: any) { }); }); + /* -------------------------------------------------------------------------- */ + /* UI: show title view */ + /* -------------------------------------------------------------------------- */ + const [mouseEnter, setMouseEnter] = React.useState(false); const onMouseEnter = React.useCallback(() => { if (!contextRef.current) { setMouseEnter(true); @@ -141,33 +70,19 @@ export default React.forwardRef(function Sider(props: ISiderProps, ref: any) { } }, [setMouseEnter]); + /* -------------------------------------------------------------------------- */ + /* UI: show search view */ + /* -------------------------------------------------------------------------- */ + const [showPanel, setShowPanel] = React.useState(false); const onShowPanel = React.useCallback(() => { setShowPanel(!showPanel); }, [showPanel, setShowPanel]); - const onStartSearch = React.useCallback( - (value: string, caseSensitive: boolean, wholeWord: boolean) => { - if (!value) { - setSearchResult([]); - } else if (treeData[0]) { - pandora && - pandora.ipcRenderer - .invoke('pandora:fileSearch', treeData[0].path, { - value, - caseSensitive, - wholeWord - }) - .then(data => { - if (data.status === 0) { - setSearchResult(data.data); - } - }); - } - setShowPanel(true); - }, - [setShowPanel, treeData] - ); - + /* -------------------------------------------------------------------------- */ + /* UI: show toc view */ + /* -------------------------------------------------------------------------- */ + const [showToc, setShowToc] = React.useState(false); + const [tocList, setTocList] = React.useState([]); const onToc = React.useCallback(() => { const tocList = getMdOutline(editor); setTocList(tocList); @@ -175,6 +90,15 @@ export default React.forwardRef(function Sider(props: ISiderProps, ref: any) { setShowToc(!showToc); }, [showToc, editor, setTocList]); + + /* -------------------------------------------------------------------------- */ + /* file */ + /* -------------------------------------------------------------------------- */ + const contextRef = React.useRef(false); + const [renameKey, setRenameKey] = React.useState(''); + const [treeData, setTreeData] = React.useState([]); + + // 获取文件列表 const getTreeData = React.useCallback(() => { pandora && pandora.ipcRenderer.invoke('pandora:dialog').then(treeData => { @@ -183,13 +107,34 @@ export default React.forwardRef(function Sider(props: ISiderProps, ref: any) { } setTreeData([]); treeData && setTreeData(treeData as ItreeData); - dispatch({ - type: 'selectedFile', - payload: treeData[0].key - }); + changeSelectedFile(treeData[0].key); }); }, []); + // 切换文件 + const onSelect = React.useCallback( + (keys: Array, {node}: Record) => { + if (isFilePath(selectedFilePath)) { + const content = editor?.getDoc().getValue() || ''; + pandora && + pandora.ipcRenderer + .invoke('pandora:writeFile', selectedFilePath, content) + .then(() => { + // TODO: 编辑状态 + }) + .catch(err => { + console.log(err); + }); + } + changeSelectedFile(keys[0] || node.path); + if (keys[0] !== renameKey) { + setRenameKey(''); + } + }, + [dispatch, renameKey, editor, selectedFilePath] + ); + + // 打开文件所在目录,新建文件,新建文件夹,删除 React.useEffect(() => { // 在finder中打开 fileEvent.removeAllListeners(FS_CREATE_FILE); @@ -213,10 +158,7 @@ export default React.forwardRef(function Sider(props: ISiderProps, ref: any) { // treeData 插入 const {node, nextTree} = addToTreeData(treeData, rootDir, 'file'); if (node.key && nextTree) { - dispatch({ - type: 'selectedFile', - payload: node.key - }); + changeSelectedFile(node.key); setTreeData(nextTree as any); } }); @@ -233,10 +175,7 @@ export default React.forwardRef(function Sider(props: ISiderProps, ref: any) { // treeData 插入 const {node, nextTree} = addToTreeData(treeData, rootDir, 'directory'); if (node.key && nextTree) { - dispatch({ - type: 'selectedFile', - payload: node.key - }); + changeSelectedFile(node.key); setTreeData(nextTree as any); } }); @@ -246,22 +185,16 @@ export default React.forwardRef(function Sider(props: ISiderProps, ref: any) { moveFileToTrash(path); const {selectedFile, nextTree} = deleteToTreeData(treeData, path); if (nextTree) { - selectedFilePath === path && - dispatch({ - type: 'selectedFile', - payload: selectedFile - }); + selectedFilePath === path && changeSelectedFile(selectedFile); setTreeData(nextTree as any); } }); }, [treeData, selectedFilePath]); + // 重命名成功之后的处理 const fsRename = React.useCallback( (key: string) => { - dispatch({ - type: 'selectedFile', - payload: key - }); + changeSelectedFile(key); setRenameKey(key); }, [setRenameKey] @@ -274,6 +207,7 @@ export default React.forwardRef(function Sider(props: ISiderProps, ref: any) { }; }, []); + // 修改 treeData const immerTreeData = React.useCallback( (nodeKey: string, name: string) => { if (treeData.length > 0) { @@ -317,10 +251,7 @@ export default React.forwardRef(function Sider(props: ISiderProps, ref: any) { .then(res => { // TODO: 数据格式 if (res.success) { - dispatch({ - type: 'selectedFile', - payload: res.data - }); + changeSelectedFile(res.data); immerTreeData(oldPath, newName); return true; } else { @@ -336,10 +267,7 @@ export default React.forwardRef(function Sider(props: ISiderProps, ref: any) { .then(res => { // TODO: 数据格式 if (res.success) { - dispatch({ - type: 'selectedFile', - payload: res.data - }); + changeSelectedFile(res.data); immerTreeData(oldPath, newName); return true; } else { @@ -354,6 +282,57 @@ export default React.forwardRef(function Sider(props: ISiderProps, ref: any) { [treeData, dispatch] ); + /* -------------------------------------------------------------------------- */ + /* toc 大纲 */ + /* -------------------------------------------------------------------------- */ + const onTocSelect = React.useCallback((item: ITocItem) => { + scrollToLine(editor, item.line); + }, [editor]); + + const onLineSelect = React.useCallback( + (line: number, _, filename: string) => { + try { + if (filename !== selectedFilePath) { + changeSelectedFile(filename); + // TODO: 中间存在一个读取的操纵,因此这里会存在bug + setTimeout(() => { + scrollToLine(editor, line); + }, 200); + } else { + scrollToLine(editor, line); + } + } catch (e) {} + }, + [editor, selectedFilePath, dispatch] + ); + + /* -------------------------------------------------------------------------- */ + /* search */ + /* -------------------------------------------------------------------------- */ + const [searchResult, setSearchResult] = React.useState([]); + const onStartSearch = React.useCallback( + (value: string, caseSensitive: boolean, wholeWord: boolean) => { + if (!value) { + setSearchResult([]); + } else if (treeData[0]) { + pandora && + pandora.ipcRenderer + .invoke('pandora:fileSearch', treeData[0].path, { + value, + caseSensitive, + wholeWord + }) + .then(data => { + if (data.status === 0) { + setSearchResult(data.data); + } + }); + } + setShowPanel(true); + }, + [setShowPanel, treeData] + ); + return (
:
没有匹配结果
From 1e37234492839b1abf3498baf6fde16532bcdaf9 Mon Sep 17 00:00:00 2001 From: BUPTlhuanyu Date: Fri, 16 Jul 2021 13:53:06 +0800 Subject: [PATCH 3/3] =?UTF-8?q?fix(=E6=8A=98=E5=8F=A0=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=A4=B9=E6=B7=BB=E5=8A=A0=E6=96=87=E4=BB=B6=E4=B8=8D=E6=AD=A3?= =?UTF-8?q?=E5=B8=B8):=20=E6=8A=98=E5=8F=A0=E6=96=87=E4=BB=B6=E5=A4=B9?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=96=87=E4=BB=B6=E4=B8=8D=E6=AD=A3=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/editor/components/sider/index.tsx | 80 +++++++++++-------- packages/workbench-electron/package.json | 2 +- 2 files changed, 48 insertions(+), 34 deletions(-) diff --git a/packages/views/src/pages/editor/components/sider/index.tsx b/packages/views/src/pages/editor/components/sider/index.tsx index 072b87f..a5f652d 100644 --- a/packages/views/src/pages/editor/components/sider/index.tsx +++ b/packages/views/src/pages/editor/components/sider/index.tsx @@ -26,6 +26,11 @@ interface ISiderProps { className: string; } +interface ISelectedItem { + target: HTMLElement | null; + node: Record; +} + export default React.forwardRef(function Sider(props: ISiderProps, ref: any) { /* -------------------------------------------------------------------------- */ /* store */ @@ -55,6 +60,7 @@ export default React.forwardRef(function Sider(props: ISiderProps, ref: any) { /* -------------------------------------------------------------------------- */ /* UI: show title view */ /* -------------------------------------------------------------------------- */ + const contextRef = React.useRef(false); const [mouseEnter, setMouseEnter] = React.useState(false); const onMouseEnter = React.useCallback(() => { if (!contextRef.current) { @@ -94,7 +100,6 @@ export default React.forwardRef(function Sider(props: ISiderProps, ref: any) { /* -------------------------------------------------------------------------- */ /* file */ /* -------------------------------------------------------------------------- */ - const contextRef = React.useRef(false); const [renameKey, setRenameKey] = React.useState(''); const [treeData, setTreeData] = React.useState([]); @@ -112,8 +117,17 @@ export default React.forwardRef(function Sider(props: ISiderProps, ref: any) { }, []); // 切换文件 + const selectedItem = React.useRef({ + target: null, + node: {} + }); const onSelect = React.useCallback( - (keys: Array, {node}: Record) => { + (keys: Array, e: Record) => { + const {node} = e; + selectedItem.current = { + target: e.nativeEvent.target as HTMLElement, + node: e.node + }; if (isFilePath(selectedFilePath)) { const content = editor?.getDoc().getValue() || ''; pandora && @@ -134,6 +148,34 @@ export default React.forwardRef(function Sider(props: ISiderProps, ref: any) { [dispatch, renameKey, editor, selectedFilePath] ); + const createItem = React.useCallback((type: 'file' | 'directory') => { + if (!treeData[0]) { + return; + } + // 清空编辑器内容 + editor && editor?.getDoc().setValue(''); + let rootDir = selectedFilePath ? selectedFilePath : treeData[0].path; + rootDir = getRootPath(rootDir); + if (!rootDir) { + return; + } + contextRef.current = true; + // [ANTD BUG] node.expanded error + selectedItem.current + && selectedItem.current.node.type === 'directory' + && selectedItem.current.node.expanded + && selectedItem.current.target + && selectedItem.current.target.click(); + // treeData 插入 + const {node, nextTree} = addToTreeData(treeData, rootDir, type); + if (node.key && nextTree) { + setTreeData(nextTree as any); + setTimeout(() => { + changeSelectedFile(node.key); + }, 0); + } + }, [treeData, selectedFilePath, editor, contextRef.current, selectedItem.current]); + // 打开文件所在目录,新建文件,新建文件夹,删除 React.useEffect(() => { // 在finder中打开 @@ -144,40 +186,12 @@ export default React.forwardRef(function Sider(props: ISiderProps, ref: any) { }); fileEvent.removeAllListeners(FS_CREATE_FILE); fileEvent.on(FS_CREATE_FILE, () => { - if (!treeData[0]) { - return; - } - // 清空编辑器内容 - editor && editor?.getDoc().setValue(''); - let rootDir = selectedFilePath ? selectedFilePath : treeData[0].path; - rootDir = getRootPath(rootDir); - if (!rootDir) { - return; - } - contextRef.current = true; - // treeData 插入 - const {node, nextTree} = addToTreeData(treeData, rootDir, 'file'); - if (node.key && nextTree) { - changeSelectedFile(node.key); - setTreeData(nextTree as any); - } + createItem('file'); }); fileEvent.removeAllListeners(FS_CREATE_DIR); fileEvent.on(FS_CREATE_DIR, () => { - if (!treeData[0]) { - return; - } - // 清空编辑器内容 - let rootDir = selectedFilePath ? selectedFilePath : treeData[0].path; - rootDir = getRootPath(rootDir); - contextRef.current = true; - // treeData 插入 - const {node, nextTree} = addToTreeData(treeData, rootDir, 'directory'); - if (node.key && nextTree) { - changeSelectedFile(node.key); - setTreeData(nextTree as any); - } + createItem('directory'); }); fileEvent.removeAllListeners(FS_DELETE); fileEvent.on(FS_DELETE, path => { @@ -189,7 +203,7 @@ export default React.forwardRef(function Sider(props: ISiderProps, ref: any) { setTreeData(nextTree as any); } }); - }, [treeData, selectedFilePath]); + }, [treeData, selectedFilePath, createItem]); // 重命名成功之后的处理 const fsRename = React.useCallback( diff --git a/packages/workbench-electron/package.json b/packages/workbench-electron/package.json index 131bc72..1bd4a83 100644 --- a/packages/workbench-electron/package.json +++ b/packages/workbench-electron/package.json @@ -1,6 +1,6 @@ { "name": "workbench-electron", - "version": "0.0.8", + "version": "0.0.9", "description": "Pandora Workbench", "productName": "Pandora Workbench", "private": true,