From b243f64227c8aaa0b11a2b0419e9d9cc2b79c5eb Mon Sep 17 00:00:00 2001 From: moonrailgun Date: Fri, 17 Dec 2021 14:21:34 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E6=8A=BD=E7=A6=BB=E7=BE=A4?= =?UTF-8?q?=E8=AF=A6=E6=83=85=E6=8B=96=E6=8B=BD=E7=9B=B8=E5=85=B3=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../GroupDetail/Panel/GroupPanelTree.tsx | 136 +---------------- .../Panel/useGroupPanelTreeDrag.tsx | 144 ++++++++++++++++++ 2 files changed, 150 insertions(+), 130 deletions(-) create mode 100644 web/src/components/modals/GroupDetail/Panel/useGroupPanelTreeDrag.tsx diff --git a/web/src/components/modals/GroupDetail/Panel/GroupPanelTree.tsx b/web/src/components/modals/GroupDetail/Panel/GroupPanelTree.tsx index d5eb5b3a..bbf0ce0e 100644 --- a/web/src/components/modals/GroupDetail/Panel/GroupPanelTree.tsx +++ b/web/src/components/modals/GroupDetail/Panel/GroupPanelTree.tsx @@ -1,19 +1,15 @@ -import React, { useCallback, useMemo, useRef } from 'react'; +import React, { useCallback, useMemo } from 'react'; import { deleteGroupPanel, GroupPanel as GroupPanelInfo, showAlert, - showToasts, t, } from 'tailchat-shared'; import { Button, Tree } from 'antd'; -import type { NodeDragEventParams } from 'rc-tree/lib/contextTypes'; -import type { DataNode, EventDataNode } from 'antd/lib/tree'; -import type { Key } from 'rc-tree/lib/interface'; -import type { AllowDrop } from 'rc-tree/lib/Tree'; -import _cloneDeep from 'lodash/cloneDeep'; -import { buildTreeDataWithGroupPanel, rebuildGroupPanelOrder } from './utils'; +import type { DataNode } from 'antd/lib/tree'; +import { buildTreeDataWithGroupPanel } from './utils'; import { Icon } from '@iconify/react'; +import { useGroupPanelTreeDrag } from './useGroupPanelTreeDrag'; interface GroupPanelTree { groupId: string; @@ -24,7 +20,6 @@ interface GroupPanelTree { * 仅同层级相互拉的树形结构 */ export const GroupPanelTree: React.FC = React.memo((props) => { - const draggingNode = useRef(null); const treeData: DataNode[] = useMemo( () => buildTreeDataWithGroupPanel(props.groupPanels), [props.groupPanels] @@ -73,127 +68,8 @@ export const GroupPanelTree: React.FC = React.memo((props) => { [handleDeletePanel] ); - const handleDragStart = useCallback( - (info: NodeDragEventParams) => { - draggingNode.current = info.node; - }, - [] - ); - - const handleDragEnd = useCallback(() => { - draggingNode.current = null; - }, []); - - const handleAllowDrop = useCallback( - ({ dropNode, dropPosition }: Parameters[0]) => { - if (draggingNode.current?.isLeaf === true) { - // 如果正在拖拽的节点是面板 - if (dropPosition === 0) { - return !dropNode.isLeaf; // 如果为分组则允许拖动 - } - return true; - } else { - // 正在拖拽的节点是分组 - if (dropPosition === 0) { - // 不允许容器之间产生父子节点 - return false; - } - - // 仅允许拖拽到分组的上下 - return !dropNode.isLeaf; - } - }, - [] - ); - - const handleDrop = useCallback( - ( - info: NodeDragEventParams & { - dragNode: EventDataNode; - dragNodesKeys: Key[]; - dropPosition: number; - dropToGap: boolean; - } - ) => { - const newGroupPanels = _cloneDeep(props.groupPanels); - const dropNodePos = newGroupPanels.findIndex( - (panel) => panel.id === info.node.key - ); - const dropGroupPanel = newGroupPanels[dropNodePos]; - if (dropNodePos === -1) { - showToasts('异常, 目标节点未找到', 'error'); - } - - const dragPanelPos = newGroupPanels.findIndex( - (panel) => panel.id === info.dragNode.key - ); - - if (draggingNode.current?.isLeaf === true) { - // 如果拖拽节点是面板且目标也是面板 - if (info.node.isLeaf === true) { - // 如果目标也是面板 - // 则更新它的父节点id为目标节点的父节点id - info.dragNodesKeys - // 获取所有的移动节点的位置 - .map((key) => newGroupPanels.findIndex((panel) => panel.id === key)) - // 过滤掉没找到的 - .filter((index) => index !== -1) - .forEach((pos) => { - newGroupPanels[pos].parentId = dropGroupPanel.parentId; - }); - } else if (info.dropToGap === false && info.node.isLeaf === false) { - // 如果目标是组节点且拖动到内部 - // 则更新它的父节点id为目标节点的id - info.dragNodesKeys - // 获取所有的移动节点的位置 - .map((key) => newGroupPanels.findIndex((panel) => panel.id === key)) - // 过滤掉没找到的 - .filter((index) => index !== -1) - .forEach((pos) => { - newGroupPanels[pos].parentId = dropGroupPanel.id; - }); - } else if (info.dropToGap === true && info.node.isLeaf === false) { - // 如果目标是组节点但是拖动到兄弟节点 - // 则更新它的父节点id为空 - info.dragNodesKeys - // 获取所有的移动节点的位置 - .map((key) => newGroupPanels.findIndex((panel) => panel.id === key)) - // 过滤掉没找到的 - .filter((index) => index !== -1) - .forEach((pos) => { - newGroupPanels[pos].parentId = undefined; - }); - } - } - - const fromPos = dragPanelPos; - let toPos: number; - if (info.node.dragOverGapTop === true) { - // 移动到目标节点之前 - toPos = dropNodePos; - } else { - // 移动到目标节点之后或之内 - toPos = dropNodePos + 1; - } - - // 应用移动, 先添加再删除 - if (fromPos < toPos) { - // 如果是将数组中前面的数拿到后面 - newGroupPanels.splice(toPos, 0, newGroupPanels[fromPos]); - newGroupPanels.splice(fromPos, 1); - } else if (fromPos > toPos) { - // 把后面的数拿到前面 - const [tmp] = newGroupPanels.splice(fromPos, 1); - newGroupPanels.splice(toPos, 0, tmp); - } - - if (typeof props.onChange === 'function') { - const res = rebuildGroupPanelOrder(newGroupPanels); - props.onChange(res); - } - }, - [props.groupPanels, props.onChange] - ); + const { handleDragStart, handleDragEnd, handleAllowDrop, handleDrop } = + useGroupPanelTreeDrag(props.groupPanels, props.onChange); return ( void +) { + const draggingNode = useRef(null); + + const handleDragStart = useCallback( + (info: NodeDragEventParams) => { + draggingNode.current = info.node; + }, + [] + ); + + const handleDragEnd = useCallback(() => { + draggingNode.current = null; + }, []); + + const handleAllowDrop = useCallback( + ({ dropNode, dropPosition }: Parameters[0]) => { + if (draggingNode.current?.isLeaf === true) { + // 如果正在拖拽的节点是面板 + if (dropPosition === 0) { + return !dropNode.isLeaf; // 如果为分组则允许拖动 + } + return true; + } else { + // 正在拖拽的节点是分组 + if (dropPosition === 0) { + // 不允许容器之间产生父子节点 + return false; + } + + // 仅允许拖拽到分组的上下 + return !dropNode.isLeaf; + } + }, + [] + ); + + const handleDrop = useCallback( + ( + info: NodeDragEventParams & { + dragNode: EventDataNode; + dragNodesKeys: Key[]; + dropPosition: number; + dropToGap: boolean; + } + ) => { + const newGroupPanels = _cloneDeep(groupPanels); + const dropNodePos = newGroupPanels.findIndex( + (panel) => panel.id === info.node.key + ); + const dropGroupPanel = newGroupPanels[dropNodePos]; + if (dropNodePos === -1) { + showToasts('异常, 目标节点未找到', 'error'); + } + + const dragPanelPos = newGroupPanels.findIndex( + (panel) => panel.id === info.dragNode.key + ); + + if (draggingNode.current?.isLeaf === true) { + // 如果拖拽节点是面板且目标也是面板 + if (info.node.isLeaf === true) { + // 如果目标也是面板 + // 则更新它的父节点id为目标节点的父节点id + info.dragNodesKeys + // 获取所有的移动节点的位置 + .map((key) => newGroupPanels.findIndex((panel) => panel.id === key)) + // 过滤掉没找到的 + .filter((index) => index !== -1) + .forEach((pos) => { + newGroupPanels[pos].parentId = dropGroupPanel.parentId; + }); + } else if (info.dropToGap === false && info.node.isLeaf === false) { + // 如果目标是组节点且拖动到内部 + // 则更新它的父节点id为目标节点的id + info.dragNodesKeys + // 获取所有的移动节点的位置 + .map((key) => newGroupPanels.findIndex((panel) => panel.id === key)) + // 过滤掉没找到的 + .filter((index) => index !== -1) + .forEach((pos) => { + newGroupPanels[pos].parentId = dropGroupPanel.id; + }); + } else if (info.dropToGap === true && info.node.isLeaf === false) { + // 如果目标是组节点但是拖动到兄弟节点 + // 则更新它的父节点id为空 + info.dragNodesKeys + // 获取所有的移动节点的位置 + .map((key) => newGroupPanels.findIndex((panel) => panel.id === key)) + // 过滤掉没找到的 + .filter((index) => index !== -1) + .forEach((pos) => { + newGroupPanels[pos].parentId = undefined; + }); + } + } + + const fromPos = dragPanelPos; + let toPos: number; + if (info.node.dragOverGapTop === true) { + // 移动到目标节点之前 + toPos = dropNodePos; + } else { + // 移动到目标节点之后或之内 + toPos = dropNodePos + 1; + } + + // 应用移动, 先添加再删除 + if (fromPos < toPos) { + // 如果是将数组中前面的数拿到后面 + newGroupPanels.splice(toPos, 0, newGroupPanels[fromPos]); + newGroupPanels.splice(fromPos, 1); + } else if (fromPos > toPos) { + // 把后面的数拿到前面 + const [tmp] = newGroupPanels.splice(fromPos, 1); + newGroupPanels.splice(toPos, 0, tmp); + } + + if (typeof onChangeGroupPanels === 'function') { + const res = rebuildGroupPanelOrder(newGroupPanels); + onChangeGroupPanels(res); + } + }, + [groupPanels, onChangeGroupPanels] + ); + + return { + handleDragStart, + handleDragEnd, + handleAllowDrop, + handleDrop, + }; +}