mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2025-01-24 12:45:18 +08:00
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:
parent
ac3df20bc7
commit
5b4dcf7803
@ -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.
|
||||
|
@ -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` 属性
|
||||
|
72
src/tree/demos/enUS/batch-render.demo.md
Normal file
72
src/tree/demos/enUS/batch-render.demo.md
Normal 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
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
@ -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. |
|
||||
|
72
src/tree/demos/zhCN/batch-render.demo.md
Normal file
72
src/tree/demos/zhCN/batch-render.demo.md
Normal 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
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
@ -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` | 是否启用虚拟滚动,启用前你需要设定好树的高度样式 |
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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')
|
||||
})
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user