feat(tree): add render-label, render-prefix and render-suffix props. (#484)

* feat(tree):  add ,  and  props.

* feat(tree): optimization

* feat: optimization
This commit is contained in:
XieZongChen 2021-07-16 00:56:51 +08:00 committed by GitHub
parent ac3df20bc7
commit 5b4dcf7803
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 257 additions and 9 deletions

View File

@ -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.

View File

@ -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` 属性

View File

@ -0,0 +1,72 @@
# Batch Render
As you can see, prefix, label, and suffix all have render functions.
```html
<n-tree
block-line
:data="data"
:default-expanded-keys="defaultExpandedKeys"
:render-prefix="renderPrefix"
:render-label="renderLabel"
:render-suffix="renderSuffix"
selectable
/>
```
```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
}
}
})
```

View File

@ -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<void>` | `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<string \| number>` | `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. |

View File

@ -0,0 +1,72 @@
# 批量渲染
如你所想,前缀、标签、后缀都可以批量渲染
```html
<n-tree
block-line
:data="data"
:default-expanded-keys="defaultExpandedKeys"
:render-prefix="renderPrefix"
:render-label="renderLabel"
:render-suffix="renderSuffix"
selectable
/>
```
```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
}
}
})
```

View File

@ -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<void>` | `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<string \| number>` | `undefined` | 如果设定则 selected 状态受控 |
| virtual-scroll | `boolean` | `false` | 是否启用虚拟滚动,启用前你需要设定好树的高度样式 |

View File

@ -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<RenderLabel>,
renderPrefix: Function as PropType<RenderPrefix>,
renderSuffix: Function as PropType<RenderSuffix>,
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,

View File

@ -216,6 +216,7 @@ const TreeNode = defineComponent({
checkable,
selectable,
selected,
checked,
highlight,
draggable,
blockLine,
@ -289,6 +290,8 @@ const TreeNode = defineComponent({
<NTreeNodeContent
ref="contentInstRef"
clsPrefix={clsPrefix}
checked={checked}
selected={selected}
onClick={
blockLine || disabled ? undefined : this.handleContentClick
}

View File

@ -1,6 +1,6 @@
import { h, defineComponent, ref, PropType } from 'vue'
import { h, defineComponent, ref, PropType, inject } from 'vue'
import { render } from '../../_utils'
import { TmNode } from './interface'
import { TmNode, treeInjectionKey } from './interface'
export default defineComponent({
name: 'TreeNodeContent',
@ -13,6 +13,8 @@ export default defineComponent({
type: Boolean,
default: false
},
checked: Boolean,
selected: Boolean,
onClick: Function as PropType<(e: MouseEvent) => 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<HTMLElement | null>(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 ? (
<div class={`${clsPrefix}-tree-node-content__prefix`}>
{render(prefix)}
{renderPrefix
? renderPrefix({
option: rawNode,
selected,
checked
})
: render(prefix)}
</div>
) : null}
<div class={`${clsPrefix}-tree-node-content__text`}>
{render(label)}
{renderLabel
? renderLabel({
option: rawNode,
selected,
checked
})
: render(label)}
</div>
{suffix ? (
{renderSuffix || suffix ? (
<div class={`${clsPrefix}-tree-node-content__suffix`}>
{render(suffix)}
{renderSuffix
? renderSuffix({
option: rawNode,
selected,
checked
})
: render(suffix)}
</div>
) : null}
</span>

View File

@ -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<null | Key>
internalScrollableRef: Ref<boolean>
internalCheckboxFocusableRef: Ref<boolean>
renderLabelRef: Ref<RenderLabel | undefined>
renderPrefixRef: Ref<RenderPrefix | undefined>
renderSuffixRef: Ref<RenderSuffix | undefined>
handleSwitcherClick: (node: TreeNode<TreeOption>) => void
handleSelect: (node: TreeNode<TreeOption>) => void
handleCheck: (node: TreeNode<TreeOption>, checked: boolean) => void

View File

@ -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')
})
})