Skip to content

Commit

Permalink
feat(comp:tree): expandIcon prop supports render function now (#1586)
Browse files Browse the repository at this point in the history
  • Loading branch information
sallerli1 committed Jul 6, 2023
1 parent 799ac59 commit f4b1a38
Show file tree
Hide file tree
Showing 12 changed files with 74 additions and 37 deletions.
4 changes: 2 additions & 2 deletions packages/components/config/src/types.ts
Expand Up @@ -44,7 +44,7 @@ import type {
import type { TabsSize } from '@idux/components/tabs'
import type { TagShape } from '@idux/components/tag'
import type { TextareaAutoRows, TextareaResize } from '@idux/components/textarea'
import type { TreeNode } from '@idux/components/tree'
import type { TreeExpandIconRenderer, TreeNode } from '@idux/components/tree'
import type { UploadFilesType, UploadIconType, UploadRequestMethod, UploadRequestOption } from '@idux/components/upload'
import type { OverlayContainerType } from '@idux/components/utils'
import type { VNode, VNodeChild } from 'vue'
Expand Down Expand Up @@ -549,7 +549,7 @@ export interface TreeConfig {
autoHeight: boolean
blocked: boolean
childrenKey: string
expandIcon: string | [string, string]
expandIcon: string | TreeExpandIconRenderer | [string, string]
draggableIcon: string
getKey: string | ((data: TreeNode<any>) => any)
labelKey: string
Expand Down
6 changes: 5 additions & 1 deletion packages/components/tree-select/docs/Api.zh.md
Expand Up @@ -24,7 +24,7 @@
| `draggableIcon` | 拖拽节点图标 | `string \| #draggableIcon` | `holder` | - | - |
| `droppable` | 是否允许放置节点,参见[TreeDroppable](/components/tree/zh#TreeDroppable) | `TreeDroppable` | - | - | - |
| `empty` | 空数据时的内容 | `'default' \| 'simple' \| [EmptyProps](/components/empty/zh#EmptyProps)` | `'simple'` | - | - |
| `expandIcon` | 树组件中的展开图标 | `string \| [string, string] \| #expandIcon="{key: VKey, expanded: boolean, node: TreeNode}"` | `right` || 当为数组时表示[`展开时图标`,`未展开时图标`] |
| `expandIcon` | 树组件中的展开图标 | `string \| TreeExpandIconRenderer \| [string, string] \| #expandIcon="{key: VKey, expanded: boolean, node: TreeNode}"` | `right` || 当为数组时表示[`展开时图标`,`未展开时图标`] |
| `getKey` | 获取数据的唯一标识 | `string \| (record: any) => VKey` | `key` || - |
| `labelKey` | 替代[TreeSelectNode](#TreeSelectNode)中的`label`字段 | `string` | `label` | ✅ | -
| `leafLineIcon` | 叶子节点的图标,用于替换默认的连接线 | `string` | - | - | 仅在 `showLine` 时生效 |
Expand Down Expand Up @@ -65,6 +65,10 @@
| `onScrolledChange` | 滚动的位置发生变化 | `(startIndex: number, endIndex: number, visibleNodes: TreeSelectNode[]) => void` | - | - |`virtual` 模式下可用 |
| `onScrolledBottom` | 滚动到底部时触发 | `() => void` | - | - |`virtual` 模式下可用 |

```ts
type TreeExpandIconRenderer = (data: { key: VKey; expanded: boolean; node: TreeNode<any> }) => VNodeChild | string
```
##### TreeSelectNode
| 名称 | 说明 | 类型 | 默认值 | 全局配置 | 备注 |
Expand Down
13 changes: 11 additions & 2 deletions packages/components/tree-select/src/types.ts
Expand Up @@ -13,7 +13,13 @@ import type { ExtractInnerPropTypes, ExtractPublicPropTypes, MaybeArray, VKey }
import type { CascaderStrategy } from '@idux/components/cascader'
import type { EmptyProps } from '@idux/components/empty'
import type { FormSize } from '@idux/components/form'
import type { TreeCustomAdditional, TreeDragDropOptions, TreeDroppable, TreeNode } from '@idux/components/tree'
import type {
TreeCustomAdditional,
TreeDragDropOptions,
TreeDroppable,
TreeExpandIconRenderer,
TreeNode,
} from '@idux/components/tree'
import type { OverlayContainerType } from '@idux/components/utils'
import type { DefineComponent, HTMLAttributes, PropType, VNode, VNodeChild } from 'vue'

Expand Down Expand Up @@ -42,7 +48,10 @@ export const treeSelectProps = {
draggableIcon: { type: String, default: undefined },
droppable: { type: Function as PropType<TreeDroppable>, default: undefined },
empty: { type: [String, Object] as PropType<'default' | 'simple' | EmptyProps>, default: 'simple' },
expandIcon: { type: [String, Array] as PropType<string | [string, string]>, default: undefined },
expandIcon: {
type: [String, Function, Array] as PropType<string | TreeExpandIconRenderer | [string, string]>,
default: undefined,
},
getKey: { type: [String, Function] as PropType<string | ((data: TreeSelectNode<any>) => any)>, default: undefined },
labelKey: { type: String, default: undefined },
leafLineIcon: { type: String, default: undefined },
Expand Down
6 changes: 5 additions & 1 deletion packages/components/tree/docs/Api.zh.md
Expand Up @@ -22,7 +22,7 @@
| `draggableIcon` | 拖拽节点图标 | `string \| #draggableIcon` | `holder` || - |
| `droppable` | 是否允许放置节点,参见[TreeDroppable](#TreeDroppable) | `TreeDroppable` | - | - | - |
| `empty` | 空数据时的内容 | `'default' \| 'simple' \| EmptyProps` | `'simple'` | - | - |
| `expandIcon` | 展开图标 | `string \| [string, string] \| #expandIcon="{key: VKey, expanded: boolean, node: TreeNode}"` | `right` || 当为数组时表示[`展开时图标`,`未展开时图标`] |
| `expandIcon` | 展开图标 | `string \| \| TreeExpandIconRenderer \| [string, string] \| #expandIcon="{key: VKey, expanded: boolean, node: TreeNode}"` | `right` || 当为数组时表示[`展开时图标`,`未展开时图标`] |
| `getKey` | 获取数据的唯一标识 | `string \| (record: any) => VKey` | `key` || - |
| `height` | 设置虚拟滚动容器高度 | `number` | - | - | - |
| `labelKey` | 替代[TreeNode](#TreeNode)中的`label`字段 | `string` | `label` | ✅ | -
Expand Down Expand Up @@ -52,6 +52,10 @@
| `onScrolledChange` | 滚动的位置发生变化 | `(startIndex: number, endIndex: number, visibleNodes: TreeNode[]) => void` | - | - |`virtual` 模式下可用 |
| `onScrolledBottom` | 滚动到底部时触发 | `() => void` | - | - |`virtual` 模式下可用 |

```ts
type TreeExpandIconRenderer = (data: { key: VKey; expanded: boolean; node: TreeNode<any> }) => VNodeChild | string
```
##### TreeNode
| 名称 | 说明 | 类型 | 默认值 | 全局配置 | 备注 |
Expand Down
1 change: 1 addition & 0 deletions packages/components/tree/index.ts
Expand Up @@ -24,5 +24,6 @@ export type {
TreeDroppableOptions,
TreeDragDropOptions,
TreeDropType,
TreeExpandIconRenderer,
CheckStrategy as TreeCheckStrategy,
} from './src/types'
25 changes: 21 additions & 4 deletions packages/components/tree/src/composables/useExpandable.ts
Expand Up @@ -5,18 +5,21 @@
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

import { type ComputedRef, type Ref, computed, ref, watch } from 'vue'
import { type ComputedRef, type Ref, computed, h, ref, watch } from 'vue'

import { isArray, isFunction, isString } from 'lodash-es'

import { VKey, callEmit, useControlledProp } from '@idux/cdk/utils'
import { type TreeConfig } from '@idux/components/config'
import { IxIcon } from '@idux/components/icon'
import { type GetKeyFn } from '@idux/components/utils'

import { type MergedNode, convertMergeNodes, convertMergedNodeMap } from './useDataSource'
import { type TreeNode, type TreeProps } from '../types'
import { type TreeExpandIconRenderer, type TreeNode, type TreeProps } from '../types'
import { callChange, getParentKeys } from '../utils'

export interface ExpandableContext {
expandIcon: ComputedRef<string | string[]>
expandIconRenderer: TreeExpandIconRenderer
expandedKeys: ComputedRef<VKey[]>
setExpandedKeys: (value: VKey[]) => void
expandAll: () => void
Expand All @@ -39,6 +42,20 @@ export function useExpandable(
const [loadedKeys, setLoadedKeys] = useControlledProp(props, 'loadedKeys', () => [])
const loadingKeys = ref<VKey[]>([])

const expandIconRenderer: TreeExpandIconRenderer = ({ key, expanded, node }) => {
if (isString(expandIcon.value)) {
return h(IxIcon, { name: expandIcon.value, rotate: expanded ? 90 : 0 })
}

if (isFunction(expandIcon.value)) {
return expandIcon.value({ key, expanded, node })
}

if (isArray(expandIcon.value)) {
return h(IxIcon, { name: expanded ? expandIcon.value[0] : expandIcon.value[1] })
}
}

const setExpandWithSearch = (searchedKeys?: VKey[]) => {
if (!searchedKeys || searchedKeys.length <= 0) {
return
Expand Down Expand Up @@ -132,5 +149,5 @@ export function useExpandable(
setExpandWithSearch(searchedKeys.value)
}

return { expandIcon, expandedKeys, setExpandedKeys, expandAll, collapseAll, handleExpand, loadingKeys }
return { expandIconRenderer, expandedKeys, setExpandedKeys, expandAll, collapseAll, handleExpand, loadingKeys }
}
25 changes: 6 additions & 19 deletions packages/components/tree/src/node/Expand.tsx
Expand Up @@ -5,12 +5,10 @@
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

import type { VNodeTypes } from 'vue'
import type { VNodeChild } from 'vue'

import { computed, defineComponent, inject, normalizeClass } from 'vue'

import { isArray } from 'lodash-es'

import { IxIcon } from '@idux/components/icon'

import { treeToken } from '../token'
Expand All @@ -19,7 +17,7 @@ import { treeNodeExpandProps } from '../types'
export default defineComponent({
props: treeNodeExpandProps,
setup(props) {
const { mergedPrefixCls, slots, expandIcon, loadingKeys, handleExpand } = inject(treeToken)!
const { mergedPrefixCls, slots, expandIconRenderer, loadingKeys, handleExpand } = inject(treeToken)!

const isLoading = computed(() => loadingKeys.value.includes(props.nodeKey))
const classes = computed(() => {
Expand All @@ -38,25 +36,14 @@ export default defineComponent({
return () => {
const prefixCls = `${mergedPrefixCls.value}-node-expand`

let children: VNodeTypes | undefined
let children: VNodeChild | undefined
if (isLoading.value) {
children = <IxIcon name="loading"></IxIcon>
} else if (!props.isLeaf) {
const { expanded } = props
if (slots.expandIcon) {
const { nodeKey: key, rawNode: node } = props
children = slots.expandIcon({ key, expanded, node })
} else {
const expandIconValue = expandIcon.value
const iconIsArray = isArray(expandIconValue)
children = (
<IxIcon
name={iconIsArray ? (expanded ? expandIconValue[0] : expandIconValue[1]) : expandIconValue}
rotate={expanded && !iconIsArray ? 90 : 0}
/>
)
}
const { expanded, nodeKey: key, rawNode: node } = props
children = (slots.expandIcon ?? expandIconRenderer)?.({ key, expanded, node })
}

return (
<span class={classes.value} onClick={onClick}>
{props.hasTopLine && <div class={`${prefixCls}-top-line`} />}
Expand Down
12 changes: 10 additions & 2 deletions packages/components/tree/src/types.ts
Expand Up @@ -12,9 +12,14 @@ import type { VirtualScrollToFn } from '@idux/cdk/scroll'
import type { ExtractInnerPropTypes, ExtractPublicPropTypes, MaybeArray, VKey } from '@idux/cdk/utils'
import type { CascaderStrategy } from '@idux/components/cascader'
import type { EmptyProps } from '@idux/components/empty'
import type { DefineComponent, HTMLAttributes, PropType } from 'vue'
import type { DefineComponent, HTMLAttributes, PropType, VNodeChild } from 'vue'

export type CheckStrategy = 'all' | 'parent' | 'child'
export type TreeExpandIconRenderer = (data: {
key: VKey
expanded: boolean
node: TreeNode<any>
}) => VNodeChild | string

export const treeProps = {
autoHeight: {
Expand Down Expand Up @@ -54,7 +59,10 @@ export const treeProps = {
draggableIcon: { type: String, default: undefined },
droppable: Function as PropType<TreeDroppable>,
empty: { type: [String, Object] as PropType<'default' | 'simple' | EmptyProps>, default: 'simple' },
expandIcon: { type: [String, Array] as PropType<string | [string, string]>, default: undefined },
expandIcon: {
type: [String, Object, Function, Array] as PropType<string | TreeExpandIconRenderer | [string, string]>,
default: undefined,
},
getKey: { type: [String, Function] as PropType<string | ((data: TreeNode<any>) => any)>, default: undefined },
height: Number,
labelKey: String,
Expand Down
6 changes: 4 additions & 2 deletions packages/pro/search/docs/Api.zh.md
Expand Up @@ -137,8 +137,8 @@ TreeSelectSearchFieldConfig
| `draggable` | 是否可拖拽 | `boolean` | - | - | 详情参考[Tree](/components/tree/zh) |
| `draggableIcon` | 拖拽图标 | `string` | - | - | 详情参考[Tree](/components/tree/zh) |
| `customDraggableIcon` | 拖拽图标自定义渲染 | `string \| () => VNodeChild` | - | - | 值为string时为对应名称的插槽 |
| `expandIcon` | 展开收起图标 | `string \| [string, string]` | - | - | 详情参考[Tree](/components/tree/zh) |
| `customExpandIcon` | 展开收起图标自定义渲染 | `string \| (options: { key: VKey, expanded: boolean, node: TreeSelectPanelData }) => VNodeChild` | - | - | 值为string时为对应名称的插槽 |
| `expandIcon` | 展开收起图标 | `string \| TreeExpandIconRenderer \| [string, string]` | - | - | 详情参考[Tree](/components/tree/zh) |
| `customExpandIcon` | 展开收起图标自定义渲染 | `string \| TreeExpandIconRenderer` | - | - | 值为string时为对应名称的插槽 |
| `showLine` | 是否展示连线 | `boolean` | - | - | 详情参考[Tree](/components/tree/zh) |
| `searchable` | 是否支持筛选 | `boolean` | `false` | - | 默认不支持 |
| `searchFn` | 搜索函数 | `(node: TreeSelectPanelData, searchValue?: string) => boolean` | - | - | 默认模糊匹配 |
Expand All @@ -157,6 +157,8 @@ TreeSelectSearchFieldConfig
| `onLoaded` | 子节点加载完毕时触发 | `((loadedKeys: any[], node: TreeSelectPanelData) => void) \| ((loadedKeys: any[], node: TreeSelectPanelData) => void)[]` | - | - | 详情参考[Tree](/components/tree/zh) |
```typescript
type TreeExpandIconRenderer = (data: { key: VKey; expanded: boolean; node: TreeNode<any> }) => VNodeChild | string

type TreeSelectPanelData = TreeSelectNode &
Required<Pick<TreeSelectNode, 'key' | 'label'>> & {
children?: TreeSelectPanelData[]
Expand Down
Expand Up @@ -24,7 +24,7 @@ exports[`ProTree > render work 1`] = `
<div class=\\"ix-tree-content\\">
<div class=\\"ix-tree-content-inner\\">
<div class=\\"ix-tree-node ix-tree-node-last\\" aria-label=\\"Node 0\\" aria-selected=\\"false\\" title=\\"Node 0\\"><span aria-hidden=\\"true\\" class=\\"ix-tree-node-indent\\"></span>
<!----><span class=\\"ix-tree-node-expand\\"><!----><i class=\\"ix-icon ix-icon-plus-square\\" style=\\"transform: rotate(0deg);\\" role=\\"img\\" aria-label=\\"plus-square\\"></i></span>
<!----><span class=\\"ix-tree-node-expand\\"><!----><i class=\\"ix-icon ix-icon-right\\" style=\\"transform: rotate(0deg);\\" role=\\"img\\" aria-label=\\"right\\"></i></span>
<!----><span class=\\"ix-tree-node-content\\"><!----><span class=\\"ix-tree-node-content-label\\">Node 0</span>
<!----></span>
</div>
Expand Down
6 changes: 5 additions & 1 deletion packages/pro/tree/docs/Api.zh.md
Expand Up @@ -25,7 +25,7 @@
| `draggableIcon` | 拖拽节点图标 | `string \| #draggableIcon` | `holder` | - | - |
| `droppable` | 是否允许放置节点,参见[TreeDroppable](#TreeDroppable) | `TreeDroppable` | - | - | - |
| `empty` | 空数据时的内容 | `'default' \| 'simple' \| EmptyProps` | `'simple'` | - | - |
| `expandIcon` | 树节点展开图标 | `string \| [string, string] \| #expandIcon="{key: VKey, expanded: boolean, node: TreeNode}"` | `['minus-square', 'plus-square']` | - | 当为数组时表示[`展开时图标`,`未展开时图标`] |
| `expandIcon` | 树节点展开图标 | `string \| TreeExpandIconRenderer \| [string, string] \| #expandIcon="{key: VKey, expanded: boolean, node: TreeNode}"` | `['minus-square', 'plus-square']` | - | 当为数组时表示[`展开时图标`,`未展开时图标`] |
| `header` | 树的头部 | `string \| HeaderProps \| #header="{expanded, onClick}"` | - | - | - |
| `height` | 设置虚拟滚动容器高度 | `number` | - | - | - |
| `getKey` | 获取数据的唯一标识 | `string \| (record: any) => VKey` | `key` | - | - |
Expand Down Expand Up @@ -54,6 +54,10 @@
| `onSearch` | 开启搜索功能后,输入后的回调 | `(searchValue: string) => void` | - | - | 通常用于服务端搜索 |
| `onNodeContextmenu` | 节点右击事件 | `(evt: Event, node: TreeNode) => void` | - | - | - |

```ts
type TreeExpandIconRenderer = (data: { key: VKey; expanded: boolean; node: TreeNode<any> }) => VNodeChild | string
```
#### ProTreeSlots
| 名称 | 说明 | 参数类型 | 备注 |
Expand Down
5 changes: 3 additions & 2 deletions packages/pro/tree/src/types.ts
Expand Up @@ -15,6 +15,7 @@ import type {
TreeCustomAdditional,
TreeDragDropOptions,
TreeDroppable,
TreeExpandIconRenderer,
TreeNode,
TreeNodeDisabled,
} from '@idux/components/tree'
Expand Down Expand Up @@ -45,8 +46,8 @@ export const proTreeProps = {
draggableIcon: { type: String, default: undefined },
droppable: { type: Function as PropType<TreeDroppable>, default: undefined },
expandIcon: {
type: [String, Array] as PropType<string | [string, string]>,
default: () => ['minus-square', 'plus-square'],
type: [String, Function, Array] as PropType<string | TreeExpandIconRenderer | [string, string]>,
default: undefined,
},
getKey: { type: [String, Function] as PropType<string | ((data: TreeNode<any>) => any)>, default: undefined },
header: { type: [String, Object] as PropType<string | HeaderProps>, default: undefined },
Expand Down

0 comments on commit f4b1a38

Please sign in to comment.