From 5b4dcf78031f8e515af2a5bda8444a2eb632f4a8 Mon Sep 17 00:00:00 2001 From: XieZongChen <46394163+amadeus711@users.noreply.github.com> Date: Fri, 16 Jul 2021 00:56:51 +0800 Subject: [PATCH] feat(tree): add `render-label`, `render-prefix` and `render-suffix` props. (#484) * feat(tree): add , and props. * feat(tree): optimization * feat: optimization --- CHANGELOG.en-US.md | 1 + CHANGELOG.zh-CN.md | 3 +- src/tree/demos/enUS/batch-render.demo.md | 72 ++++++++++++++++++++++++ src/tree/demos/enUS/index.demo-entry.md | 4 ++ src/tree/demos/zhCN/batch-render.demo.md | 72 ++++++++++++++++++++++++ src/tree/demos/zhCN/index.demo-entry.md | 4 ++ src/tree/src/Tree.tsx | 11 +++- src/tree/src/TreeNode.tsx | 3 + src/tree/src/TreeNodeContent.tsx | 46 ++++++++++++--- src/tree/src/interface.ts | 21 +++++++ src/tree/tests/Tree.spec.ts | 29 ++++++++++ 11 files changed, 257 insertions(+), 9 deletions(-) create mode 100644 src/tree/demos/enUS/batch-render.demo.md create mode 100644 src/tree/demos/zhCN/batch-render.demo.md diff --git a/CHANGELOG.en-US.md b/CHANGELOG.en-US.md index 435402578..1f22e0799 100644 --- a/CHANGELOG.en-US.md +++ b/CHANGELOG.en-US.md @@ -4,6 +4,7 @@ ### Feats +- `n-tree` add `render-label`, `render-prefix` and `render-suffix` props. - `n-rate` add `allow-half` prop. - `n-carousel` add `show-arrow` prop. - `n-slider` add `format-tooltip` prop. diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md index 59ba15ac1..beb074519 100644 --- a/CHANGELOG.zh-CN.md +++ b/CHANGELOG.zh-CN.md @@ -4,9 +4,10 @@ ### Feats +- `n-tree` 新增 `render-label`、`render-prefix` 和 `render-suffix` 属性 - `n-rate` 新增 `allow-half` 属性 - `n-carousel` 新增 `show-arrow` 属性 -- `n-slider` 新增 `format-tooltip` 属性. +- `n-slider` 新增 `format-tooltip` 属性 - `n-upload` 在 `on-finish` 回调参数中新增 `event` - `n-slider` 新增 `format-tooltip` 属性 - `n-rate` 新增 `readonly` 属性 diff --git a/src/tree/demos/enUS/batch-render.demo.md b/src/tree/demos/enUS/batch-render.demo.md new file mode 100644 index 000000000..347b0a984 --- /dev/null +++ b/src/tree/demos/enUS/batch-render.demo.md @@ -0,0 +1,72 @@ +# Batch Render + +As you can see, prefix, label, and suffix all have render functions. + +```html + +``` + +```js +import { h, defineComponent, ref } from 'vue' +import { NButton } from 'naive-ui' + +function createData (level = 4, baseKey = '') { + if (!level) return undefined + return Array.apply(null, { length: 6 - level }).map((_, index) => { + const key = '' + baseKey + level + index + return { + label: createLabel(level), + key, + children: createData(level - 1, key), + level + } + }) +} + +function createLabel (level) { + if (level === 4) return 'Out of Tao, One is born' + if (level === 3) return 'Out of One, Two' + if (level === 2) return 'Out of Two, Three' + if (level === 1) return 'Out of Three, the created universe' +} + +function renderPrefix ({ option }) { + return h( + NButton, + { text: true, type: 'primary' }, + { default: () => `Prefix-${option.level}` } + ) +} + +function renderLabel ({ option }) { + return `${option.label} ^_^` +} + +function renderSuffix ({ option }) { + return h( + NButton, + { text: true, type: 'primary' }, + { default: () => `Suffix-${option.level}` } + ) +} + +export default defineComponent({ + setup () { + return { + data: createData(), + defaultExpandedKeys: ref(['40', '41']), + renderPrefix, + renderLabel, + renderSuffix + } + } +}) +``` diff --git a/src/tree/demos/enUS/index.demo-entry.md b/src/tree/demos/enUS/index.demo-entry.md index 1798b9f7e..0ffd3ba4c 100644 --- a/src/tree/demos/enUS/index.demo-entry.md +++ b/src/tree/demos/enUS/index.demo-entry.md @@ -16,6 +16,7 @@ virtual async disabled prefix-and-suffix +batch-render ``` ## API @@ -44,6 +45,9 @@ prefix-and-suffix | on-load | `(node: TreeOption) => Promise` | `undefined` | | | pattern | `string` | `''` | | | remote | `boolean` | `false` | Whether to load nodes async. It should work with `on-load` | +| render-label | `(info: {option: TreeOption, checked: boolean, selected: boolean}) => VNodeChild` | `undefined` | Render function of all the options' label. | +| render-prefix | `(info: {option: TreeOption, checked: boolean, selected: boolean}) => VNodeChild` | `undefined` | Render function of all the options' prefix. | +| render-suffix | `(info: {option: TreeOption, checked: boolean, selected: boolean}) => VNodeChild` | `undefined` | Render function of all the options' suffix. | | selectable | `boolean` | `true` | | | selected-keys | `Array` | `undefined` | If set, selected status will work in controlled manner. | | virtual-scroll | `boolean` | `false` | Whether to enable virtual scroll. You need to set proper style height of the tree in advance. | diff --git a/src/tree/demos/zhCN/batch-render.demo.md b/src/tree/demos/zhCN/batch-render.demo.md new file mode 100644 index 000000000..5f45dacae --- /dev/null +++ b/src/tree/demos/zhCN/batch-render.demo.md @@ -0,0 +1,72 @@ +# 批量渲染 + +如你所想,前缀、标签、后缀都可以批量渲染 + +```html + +``` + +```js +import { h, defineComponent, ref } from 'vue' +import { NButton } from 'naive-ui' + +function createData (level = 4, baseKey = '') { + if (!level) return undefined + return Array.apply(null, { length: 6 - level }).map((_, index) => { + const key = '' + baseKey + level + index + return { + label: createLabel(level), + key, + children: createData(level - 1, key), + level + } + }) +} + +function createLabel (level) { + if (level === 4) return '道生一' + if (level === 3) return '一生二' + if (level === 2) return '二生三' + if (level === 1) return '三生万物' +} + +function renderPrefix ({ option }) { + return h( + NButton, + { text: true, type: 'primary' }, + { default: () => `Prefix-${option.level}` } + ) +} + +function renderLabel ({ option }) { + return `${option.label} ^_^` +} + +function renderSuffix ({ option }) { + return h( + NButton, + { text: true, type: 'primary' }, + { default: () => `Suffix-${option.level}` } + ) +} + +export default defineComponent({ + setup () { + return { + data: createData(), + defaultExpandedKeys: ref(['40', '41']), + renderPrefix, + renderLabel, + renderSuffix + } + } +}) +``` diff --git a/src/tree/demos/zhCN/index.demo-entry.md b/src/tree/demos/zhCN/index.demo-entry.md index ca0b5d5a2..ce9cdeb01 100644 --- a/src/tree/demos/zhCN/index.demo-entry.md +++ b/src/tree/demos/zhCN/index.demo-entry.md @@ -16,6 +16,7 @@ virtual async disabled prefix-and-suffix +batch-render ``` ## API @@ -45,6 +46,9 @@ prefix-and-suffix | on-load | `(node: TreeOption) => Promise` | `undefined` | | | pattern | `string` | `''` | | | remote | `boolean` | `false` | 是否异步获取选项,和 onLoad 配合 | +| render-label | `(info: {option: TreeOption, checked: boolean, selected: boolean}) => VNodeChild` | `undefined` | 节点内容的渲染函数 | +| render-prefix | `(info: {option: TreeOption, checked: boolean, selected: boolean}) => VNodeChild` | `undefined` | 节点前缀的渲染函数 | +| render-suffix | `(info: {option: TreeOption, checked: boolean, selected: boolean}) => VNodeChild` | `undefined` | 节点后缀的渲染函数 | | selectable | `boolean` | `true` | | | selected-keys | `Array` | `undefined` | 如果设定则 selected 状态受控 | | virtual-scroll | `boolean` | `false` | 是否启用虚拟滚动,启用前你需要设定好树的高度样式 | diff --git a/src/tree/src/Tree.tsx b/src/tree/src/Tree.tsx index f92c80488..73b676809 100644 --- a/src/tree/src/Tree.tsx +++ b/src/tree/src/Tree.tsx @@ -40,7 +40,10 @@ import { AllowDrop, MotionData, treeInjectionKey, - InternalTreeInst + InternalTreeInst, + RenderLabel, + RenderPrefix, + RenderSuffix } from './interface' import MotionWrapper from './MotionWrapper' import { defaultAllowDrop } from './dnd' @@ -132,6 +135,9 @@ const treeProps = { default: true }, virtualScroll: Boolean, + renderLabel: Function as PropType, + renderPrefix: Function as PropType, + renderSuffix: Function as PropType, onDragenter: [Function, Array] as PropType< MaybeArray<(e: TreeDragInfo) => void> >, @@ -981,6 +987,9 @@ export default defineComponent({ pendingNodeKeyRef, internalScrollableRef: toRef(props, 'internalScrollable'), internalCheckboxFocusableRef: toRef(props, 'internalCheckboxFocusable'), + renderLabelRef: toRef(props, 'renderLabel'), + renderPrefixRef: toRef(props, 'renderPrefix'), + renderSuffixRef: toRef(props, 'renderSuffix'), handleSwitcherClick, handleDragEnd, handleDragEnter, diff --git a/src/tree/src/TreeNode.tsx b/src/tree/src/TreeNode.tsx index 9eb42476b..e4ab0cf5b 100644 --- a/src/tree/src/TreeNode.tsx +++ b/src/tree/src/TreeNode.tsx @@ -216,6 +216,7 @@ const TreeNode = defineComponent({ checkable, selectable, selected, + checked, highlight, draggable, blockLine, @@ -289,6 +290,8 @@ const TreeNode = defineComponent({ void>, onDragstart: Function as PropType<(e: DragEvent) => void>, tmNode: { @@ -21,6 +23,9 @@ export default defineComponent({ } }, setup (props) { + const { renderLabelRef, renderPrefixRef, renderSuffixRef } = + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + inject(treeInjectionKey)! const selfRef = ref(null) function doClick (e: MouseEvent): void { const { onClick } = props @@ -31,15 +36,24 @@ export default defineComponent({ } return { selfRef, + renderLabel: renderLabelRef, + renderPrefix: renderPrefixRef, + renderSuffix: renderSuffixRef, handleClick } }, render () { const { clsPrefix, + checked = false, + selected = false, + renderLabel, + renderPrefix, + renderSuffix, handleClick, onDragstart, tmNode: { + rawNode, rawNode: { prefix, label, suffix } } } = this @@ -51,17 +65,35 @@ export default defineComponent({ draggable={onDragstart === undefined ? undefined : true} onDragstart={onDragstart} > - {prefix ? ( + {renderPrefix || prefix ? (
- {render(prefix)} + {renderPrefix + ? renderPrefix({ + option: rawNode, + selected, + checked + }) + : render(prefix)}
) : null}
- {render(label)} + {renderLabel + ? renderLabel({ + option: rawNode, + selected, + checked + }) + : render(label)}
- {suffix ? ( + {renderSuffix || suffix ? (
- {render(suffix)} + {renderSuffix + ? renderSuffix({ + option: rawNode, + selected, + checked + }) + : render(suffix)}
) : null} diff --git a/src/tree/src/interface.ts b/src/tree/src/interface.ts index 8de1e286d..4e27303f6 100644 --- a/src/tree/src/interface.ts +++ b/src/tree/src/interface.ts @@ -20,6 +20,24 @@ export type TreeOption = TreeOptionBase & { [k: string]: unknown } export type TreeOptions = TreeOption[] +export interface TreeRenderProps { + option: TreeOption + checked: boolean + selected: boolean +} + +type RenderTreePart = ({ + option, + checked, + selected +}: TreeRenderProps) => VNodeChild + +export type RenderLabel = RenderTreePart + +export type RenderPrefix = RenderTreePart + +export type RenderSuffix = RenderTreePart + export interface TreeDragInfo { event: DragEvent node: TreeOption @@ -78,6 +96,9 @@ export interface TreeInjection { pendingNodeKeyRef: Ref internalScrollableRef: Ref internalCheckboxFocusableRef: Ref + renderLabelRef: Ref + renderPrefixRef: Ref + renderSuffixRef: Ref handleSwitcherClick: (node: TreeNode) => void handleSelect: (node: TreeNode) => void handleCheck: (node: TreeNode, checked: boolean) => void diff --git a/src/tree/tests/Tree.spec.ts b/src/tree/tests/Tree.spec.ts index 372a80fee..c986774d1 100644 --- a/src/tree/tests/Tree.spec.ts +++ b/src/tree/tests/Tree.spec.ts @@ -63,4 +63,33 @@ describe('n-tree', () => { expect(wrapper.find('.n-tree-node-content__suffix').exists()).toBe(true) expect(wrapper.find('.n-tree-node-content__suffix').text()).toBe('suffix') }) + + it('should work with `render-label`, `render-prefix` and `render-suffix`', async () => { + const wrapper = mount(NTree, { + props: { + data: [ + { + label: 'test', + key: '123', + children: [ + { + label: '123', + key: '123' + } + ] + } + ], + renderPrefix: () => 'prefix', + renderLabel: () => 'label', + renderSuffix: () => 'suffix' + } + }) + + expect(wrapper.find('.n-tree-node-content__prefix').exists()).toBe(true) + expect(wrapper.find('.n-tree-node-content__prefix').text()).toBe('prefix') + expect(wrapper.find('.n-tree-node-content__text').exists()).toBe(true) + expect(wrapper.find('.n-tree-node-content__text').text()).toBe('label') + expect(wrapper.find('.n-tree-node-content__suffix').exists()).toBe(true) + expect(wrapper.find('.n-tree-node-content__suffix').text()).toBe('suffix') + }) })