Skip to content

Commit

Permalink
Merge pull request #2582 from XiaoMi/feature/tree(#2581)
Browse files Browse the repository at this point in the history
feat(tree): #2581
  • Loading branch information
solarjoker authored Sep 7, 2023
2 parents 1cba1c1 + 8205d33 commit 506d540
Show file tree
Hide file tree
Showing 8 changed files with 207 additions and 57 deletions.
5 changes: 5 additions & 0 deletions .changeset/fast-rocks-tell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hi-ui/tree": minor
---

feat: 增加 actionRender API
5 changes: 5 additions & 0 deletions .changeset/rude-lamps-bathe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hi-ui/hiui": patch
---

Tree feat: 增加 actionRender API
22 changes: 14 additions & 8 deletions packages/ui/tree/src/styles/editable-tree.scss
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,32 @@ $prefix: '#{$component-prefix}-tree' !default;
width: 38px;
}

.#{$prefix}-action-btn {
position: relative;
color: use-color('gray', 800);
.#{$prefix}-action-wrap {
transition: opacity 0.3s;
opacity: 0;
visibility: hidden;

&:hover {
color: use-color-mode('primary');
}

&--visible {
visibility: visible;
opacity: 1;

.#{$prefix}-action-btn {
color: use-color-mode('primary');
}
}
}

.#{$prefix}-action-btn {
position: relative;
color: use-color('gray', 800);

&:hover {
color: use-color-mode('primary');
}
}

.#{$prefix}-node:hover {
.#{$prefix}-action-btn {
.#{$prefix}-action-wrap {
visibility: visible;
opacity: 1;
}
Expand Down
4 changes: 4 additions & 0 deletions packages/ui/tree/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ export type TreeEditActions = {
* 执行删除节点操作
*/
deleteNode: () => void
/**
* 执行打开菜单操作
*/
openMenu: () => void
/**
* 执行关闭菜单操作
*/
Expand Down
119 changes: 70 additions & 49 deletions packages/ui/tree/src/use-tree-action.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import React, { useState, useCallback, useMemo, useRef, forwardRef } from 'react'
import { __DEV__ } from '@hi-ui/env'
import { cx } from '@hi-ui/classname'
import { useOutsideClick } from '@hi-ui/use-outside-click'
import { useMergeRefs } from '@hi-ui/use-merge-refs'
import { useToggle, UseToggleAction } from '@hi-ui/use-toggle'
import { useLatestRef } from '@hi-ui/use-latest'
import { isArrayNonEmpty, isFunction } from '@hi-ui/type-assertion'
import Input from '@hi-ui/input'
import Popper from '@hi-ui/popper'
import { CheckOutlined, CloseOutlined } from '@hi-ui/icons'
import { TreeProps, Tree, treePrefix } from './Tree'
import {
FlattedTreeNodeData,
Expand All @@ -9,20 +17,12 @@ import {
TreeDataStatus,
TreeMenuActionOption,
TreeNodeEventData,
TreeEditActions,
} from './types'
import { useEdit, useCache, useExpandProps } from './hooks'
import { flattenTreeData } from './utils'
import Input from '@hi-ui/input'
import { useOutsideClick } from '@hi-ui/use-outside-click'
import { useMergeRefs } from '@hi-ui/use-merge-refs'
import { useToggle, UseToggleAction } from '@hi-ui/use-toggle'
import { useLatestRef } from '@hi-ui/use-latest'
import Popper from '@hi-ui/popper'
import { CheckOutlined, CloseOutlined } from '@hi-ui/icons'
// import Button from '@hi-ui/button'
import { IconButton } from './IconButton'
import { defaultActionIcon } from './icons'
import { isArrayNonEmpty, isFunction } from '@hi-ui/type-assertion'

import './styles/editable-tree.scss'

Expand Down Expand Up @@ -73,6 +73,7 @@ export const useTreeEditProps = <T extends EditableTreeProps>(
menuOptions,
editPlaceholder: placeholder,
fieldNames,
actionRender,
...nativeTreeProps
} = props
const [treeData, setTreeData] = useCache(data)
Expand Down Expand Up @@ -118,6 +119,7 @@ export const useTreeEditProps = <T extends EditableTreeProps>(
expandedIds={expandedIds}
focusTree={focusTree}
onExpand={tryToggleExpandedIds}
actionRender={actionRender}
/>
)
}
Expand Down Expand Up @@ -185,10 +187,14 @@ export interface EditableTreeProps extends TreeProps {
* 输入框占位符
*/
editPlaceholder?: string
/**
* 自定义可编辑树操作项
*/
actionRender?: (node: FlattedTreeNodeData, editActions: TreeEditActions) => React.ReactNode
}

const EditableTreeNodeTitle = (props: EditableTreeNodeTitleProps) => {
const { prefixCls, node, title } = props
const { prefixCls, node, title, actionRender } = props

// 如果是添加节点,则进入节点编辑临时态
const [editing, editingAction] = useToggle(() => node.raw.type === TreeNodeType.ADD || false)
Expand All @@ -200,7 +206,7 @@ const EditableTreeNodeTitle = (props: EditableTreeNodeTitleProps) => {
return (
<div className={`${prefixCls}__title`}>
<span className={`${prefixCls}__title-text`}>{title || node.title}</span>
<EditableNodeMenu {...props} editingAction={editingAction} />
<EditableNodeMenu {...props} editingAction={editingAction} actionRender={actionRender} />
</div>
)
}
Expand All @@ -219,6 +225,7 @@ interface EditableTreeNodeTitleProps {
placeholder?: string
menuOptions?: TreeMenuActionOption[] | ((node: FlattedTreeNodeData) => TreeMenuActionOption[])
focusTree: () => void
actionRender?: (node: FlattedTreeNodeData, editActions: TreeEditActions) => React.ReactNode | null
}

const EditableNodeMenu = (props: EditableNodeMenuProps) => {
Expand All @@ -232,6 +239,7 @@ const EditableNodeMenu = (props: EditableNodeMenuProps) => {
expandedIds,
onExpand,
menuOptions: menuOptionsProp,
actionRender,
} = props

const [menuVisible, menuVisibleAction] = useToggle(false)
Expand Down Expand Up @@ -269,6 +277,9 @@ const EditableNodeMenu = (props: EditableNodeMenuProps) => {
menuVisibleAction.off()
addSiblingNode(node)
},
openMenu: () => {
menuVisibleAction.on()
},
closeMenu: () => {
menuVisibleAction.off()
},
Expand Down Expand Up @@ -297,44 +308,54 @@ const EditableNodeMenu = (props: EditableNodeMenuProps) => {
if (!isArrayNonEmpty(menuOptions)) return null

return (
<>
<IconButton
tabIndex={-1}
className={cx(`${prefixCls}-action-btn`, menuVisible && `${prefixCls}-action-btn--visible`)}
// ref={useMergeRefs(setTargetElRef, setTargetEl)}
ref={setTargetElRef}
icon={defaultActionIcon}
onClick={(evt) => {
// 阻止冒泡,避免触发节点选中
evt.stopPropagation()
menuVisibleAction.not()
}}
/>
<Popper
crossGap={8}
className={`${prefixCls}__popper`}
placement="bottom-end"
visible={!!menuOptions && menuVisible}
onClose={menuVisibleAction.off}
attachEl={targetElRef}
>
<ul className={`${prefixCls}-action`}>
{menuOptions.map((option, idx) => (
<li
key={idx}
className={`${prefixCls}-action__item`}
onClick={(evt) => {
// 阻止冒泡,避免触发节点选中
evt.stopPropagation()
handleMenuClick(node, option)
}}
>
{option.title}
</li>
))}
</ul>
</Popper>
</>
<div
className={cx(`${prefixCls}-action-wrap`, menuVisible && `${prefixCls}-action-wrap--visible`)}
onClick={(evt) => {
// 阻止冒泡,避免触发节点选中
evt.stopPropagation()
}}
>
{isFunction(actionRender) ? (
actionRender(node, menuActionsRef.current)
) : (
<>
<IconButton
tabIndex={-1}
className={cx(`${prefixCls}-action-btn`)}
// ref={useMergeRefs(setTargetElRef, setTargetEl)}
ref={setTargetElRef}
icon={defaultActionIcon}
onClick={() => {
menuVisibleAction.not()
}}
/>
<Popper
crossGap={8}
className={`${prefixCls}__popper`}
placement="bottom-end"
visible={!!menuOptions && menuVisible}
onClose={menuVisibleAction.off}
attachEl={targetElRef}
>
<ul className={`${prefixCls}-action`}>
{menuOptions.map((option, idx) => (
<li
key={idx}
className={`${prefixCls}-action__item`}
onClick={(evt) => {
// 阻止冒泡,避免触发节点选中
evt.stopPropagation()
handleMenuClick(node, option)
}}
>
{option.title}
</li>
))}
</ul>
</Popper>
</>
)}
</div>
)
}

Expand Down
107 changes: 107 additions & 0 deletions packages/ui/tree/stories/action-render.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import React from 'react'
import Tree, { useTreeAction } from '../src'
import Space from '@hi-ui/space'
import PopConfirm from '@hi-ui/pop-confirm'
import { PlusOutlined, DuplicateOutlined, EditOutlined, DeleteOutlined } from '@hi-ui/icons'

/**
* @title 自定义可编辑树操作项
* @desc 用于操作菜单放在外面的场景
*/
export const ActionRender = () => {
const ActionTree = useTreeAction(Tree)

return (
<>
<h1>ActionRender for Tree</h1>
<div className="tree-action-render__wrap">
<ActionTree
expandOnSelect
editPlaceholder="请填写菜单"
actionRender={(node, editActions) => {
console.log('node', node)

const { id } = node

return id === 11 ? (
<Space>
<PlusOutlined onClick={() => editActions.addChildNode()} />
<DuplicateOutlined onClick={() => editActions.addSiblingNode()} />
<EditOutlined onClick={() => editActions.editNode()} />
<PopConfirm
title={'确认删除该节点?'}
onConfirm={editActions.deleteNode}
onClose={editActions.closeMenu}
>
<DeleteOutlined onClick={() => editActions.openMenu()} />
</PopConfirm>
</Space>
) : null
}}
menuOptions={[
{
type: 'addChildNode',
title: '新建子节点',
},
{
type: 'addSiblingNode',
title: '新建兄弟节点',
},
{
// type: 'deleteNode',
title: '删除当前菜单',
onClick(node, action) {
action.closeMenu()

Modal.confirm({
title: '提示',
content: '确定删除吗?',
onConfirm: () => {
action.deleteNode()
},
})
},
},
{
type: 'editNode',
title: '编辑当前菜单',
},
{
title: 'Hello,自定义的菜单',
onClick(node, action) {
console.log(node)
action.closeMenu()
},
},
]}
data={[
{
id: 1,
title: '小米',
children: [
{
id: 2,
title: '研发',
children: [
{ id: 3, title: '后端' },
{ id: 4, title: '运维' },
{ id: 5, title: '前端' },
],
},
{ id: 6, title: '产品' },
],
},
{
id: 11,
title: '大米',
children: [
{ id: 22, title: '可视化' },
{ id: 66, title: 'HiUI' },
],
},
]}
/>
</div>
</>
)
}
1 change: 1 addition & 0 deletions packages/ui/tree/stories/editable.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const Editable = () => {
<h1>Editable for Tree</h1>
<div className="tree-editable__wrap">
<ActionTree
expandOnSelect
editPlaceholder="请填写菜单"
menuOptions={[
{
Expand Down
1 change: 1 addition & 0 deletions packages/ui/tree/stories/index.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export * from './checkable.stories'
export * from './expand.stories'
export * from './virtual-list.stories'
export * from './editable.stories'
export * from './action-render.stories'
export * from './search.stories'
export * from './custom-search.stories'
export * from './custom-title.stories'
Expand Down

0 comments on commit 506d540

Please sign in to comment.