Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Tree): support allowDrop API #3098

Merged
merged 1 commit into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/tree/Tree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ const Tree = forwardRef<TreeInstanceFunctions<TreeOptionData>, TreeProps>((origi

// 可见节点集合
const [visibleNodes, setVisibleNodes] = useState([]);

const {
empty,
activable,
Expand All @@ -65,6 +64,7 @@ const Tree = forwardRef<TreeInstanceFunctions<TreeOptionData>, TreeProps>((origi
scroll,
className,
style,
allowDrop,
} = props;

const { value, onChange, expanded, onExpand, onActive, actived } = useControllable(props);
Expand Down Expand Up @@ -290,6 +290,7 @@ const Tree = forwardRef<TreeInstanceFunctions<TreeOptionData>, TreeProps>((origi
icon={icon}
label={label}
line={line}
allowDrop={allowDrop}
transition={transition}
expandOnClickNode={expandOnClickNode}
activable={activable}
Expand Down Expand Up @@ -323,6 +324,7 @@ const Tree = forwardRef<TreeInstanceFunctions<TreeOptionData>, TreeProps>((origi
icon={icon}
label={label}
line={line}
allowDrop={allowDrop}
transition={transition}
expandOnClickNode={expandOnClickNode}
activable={activable}
Expand Down
4 changes: 4 additions & 0 deletions src/tree/TreeItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const TreeItem = forwardRef(
onTreeItemMounted?: (rowData: { ref: HTMLElement; data: TreeNode }) => void;
isVirtual?: boolean;
keys: TdTreeProps['keys'];
allowDrop?: TdTreeProps['allowDrop'];
},
ref: React.Ref<HTMLDivElement>,
) => {
Expand All @@ -54,6 +55,7 @@ const TreeItem = forwardRef(
onChange,
isVirtual,
onTreeItemMounted,
allowDrop,
} = props;

const { CaretRightSmallIcon } = useGlobalIcon({
Expand Down Expand Up @@ -308,6 +310,7 @@ const TreeItem = forwardRef(
const { setDragStatus, isDragging, dropPosition, isDragOver } = useDraggable({
node,
nodeRef,
allowDrop,
});

const handleDragStart: DragEventHandler<HTMLDivElement> = (evt: DragEvent<HTMLDivElement>) => {
Expand Down Expand Up @@ -344,6 +347,7 @@ const TreeItem = forwardRef(
};
const handleDrop: DragEventHandler<HTMLDivElement> = (evt: DragEvent<HTMLDivElement>) => {
const { node } = props;

if (!node.isDraggable()) return;
evt.stopPropagation();
evt.preventDefault();
Expand Down
10 changes: 9 additions & 1 deletion src/tree/_example/draggable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const items = [
},
{
value: '2.2',
label: '2.2',
label: '2.2 不允许拖放为 2.2 的子节点',
},
],
},
Expand All @@ -77,6 +77,13 @@ export default () => {
console.log(dragNode, dropPosition, e);
};

const handleAllowDrop: TreeProps['allowDrop'] = (ctx) => {
const { dropNode, dropPosition } = ctx;
if (dropNode.value === '2.2' && dropPosition === 0) {
return false;
}
};

return (
<Space direction="vertical">
<Tree
Expand All @@ -91,6 +98,7 @@ export default () => {
onDragEnd={handleDragEnd}
onDragLeave={handleDragLeave}
onDragOver={handleDragOver}
allowDrop={handleAllowDrop}
/>
</Space>
);
Expand Down
18 changes: 17 additions & 1 deletion src/tree/hooks/TreeDraggableContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import TreeNode from '../../_common/js/tree-v1/tree-node';
import { TreeProps } from '../Tree';
import { createHookContext } from '../../_util/createHookContext';

import type { TdTreeProps } from '../type';

interface Value {
props: TreeProps;
store: TreeStore;
Expand Down Expand Up @@ -44,14 +46,28 @@ export const TreeDraggableContext = createHookContext((value: Value) => {
});
};

const onDrop = (context: { node: TreeNode; dropPosition: number; e: DragEvent<HTMLDivElement> }) => {
const onDrop = (context: {
node: TreeNode;
dropPosition: number;
e: DragEvent<HTMLDivElement>;
allowDrop?: TdTreeProps['allowDrop'];
}) => {
const { node, dropPosition } = context;
if (
node.value === dragNode.current?.value ||
node.getParents().some((_node) => _node.value === dragNode.current?.value)
) {
return;
}
const ctx = {
dropNode: node.getModel(),
dragNode: dragNode.current.getModel(),
dropPosition,
e: context.e,
};

if (props.allowDrop?.(ctx) === false) return;

const nodes = store.getNodes() as TreeNode[];
nodes.some((_node) => {
if (_node.value === node.value) {
Expand Down
12 changes: 9 additions & 3 deletions src/tree/hooks/useDraggable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@ import { useTreeDraggableContext } from './TreeDraggableContext';
import { DropPosition } from '../interface';
import { usePersistFn } from '../../hooks/usePersistFn';

export default function useDraggable(props: { nodeRef: RefObject<HTMLElement | undefined>; node: TreeNode }) {
const { nodeRef, node } = props;
import type { TdTreeProps } from '../type';

export default function useDraggable(props: {
nodeRef: RefObject<HTMLElement | undefined>;
node: TreeNode;
allowDrop?: TdTreeProps['allowDrop'];
}) {
const { nodeRef, node, allowDrop } = props;
const { onDragStart, onDragEnd, onDragLeave, onDragOver, onDrop } = useTreeDraggableContext();

const [state, setState] = useState<{
Expand Down Expand Up @@ -83,7 +89,7 @@ export default function useDraggable(props: { nodeRef: RefObject<HTMLElement | u
onDragLeave?.({ node, e });
break;
case 'drop':
onDrop?.({ node, dropPosition: state.dropPosition, e });
onDrop?.({ node, dropPosition: state.dropPosition, e, allowDrop });
setPartialState({
isDragOver: false,
});
Expand Down
1 change: 1 addition & 0 deletions src/tree/tree.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ style | Object | - | 样式,Typescript:`React.CSSProperties` | N
activable | Boolean | false | \- | N
activeMultiple | Boolean | false | \- | N
actived | Array | - | Typescript:`Array<TreeNodeValue>` | N
allowDrop | Function | - | Determine whether the node can execute the drop operation。Typescript:`(context: { e: DragEvent; dragNode: TreeNodeModel<T>; dropNode: TreeNodeModel<T>; dropPosition: number; }) => boolean` | N
allowFoldNodeOnFilter | Boolean | false | \- | N
checkProps | Object | - | Typescript:`CheckboxProps`,[Checkbox API Documents](./checkbox?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-react/blob/develop/src/tree/type.ts) | N
checkStrictly | Boolean | false | \- | N
Expand Down
1 change: 1 addition & 0 deletions src/tree/tree.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ style | Object | - | 样式,TS 类型:`React.CSSProperties` | N
activable | Boolean | false | 节点是否可高亮 | N
activeMultiple | Boolean | false | 是否允许多个节点同时高亮 | N
actived | Array | - | 高亮的节点值。TS 类型:`Array<TreeNodeValue>` | N
allowDrop | Function | - | 判断节点是否可以执行 drop 操作,泛型 `T` 表示树节点 TS 类型。TS 类型:`(context: { e: DragEvent; dragNode: TreeNodeModel<T>; dropNode: TreeNodeModel<T>; dropPosition: number; }) => boolean` | N
allowFoldNodeOnFilter | Boolean | false | 是否允许在过滤时节点折叠节点 | N
checkProps | Object | - | 透传属性到 checkbox 组件。参考 checkbox 组件 API。TS 类型:`CheckboxProps`,[Checkbox API Documents](./checkbox?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-react/blob/develop/src/tree/type.ts) | N
checkStrictly | Boolean | false | 父子节点选中状态不再关联,可各自选中或取消 | N
Expand Down
9 changes: 9 additions & 0 deletions src/tree/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ export interface TdTreeProps<T extends TreeOptionData = TreeOptionData> {
* 高亮的节点值,非受控属性
*/
defaultActived?: Array<TreeNodeValue>;
/**
* 判断节点是否可以执行 drop 操作,泛型 `T` 表示树节点 TS 类型
*/
allowDrop?: (context: {
e: DragEvent;
dragNode: TreeNodeModel<T>;
dropNode: TreeNodeModel<T>;
dropPosition: number;
}) => boolean;
/**
* 是否允许在过滤时节点折叠节点
* @default false
Expand Down
Loading