feat(tree, tree-select): add leaf-only prop (#313)

* feat(tree and tree-select): add leaf-only prop

* feat(tree and tree-select): add leaf-only prop

* feat(tree and tree-select): add leaf-only prop

* feat(tree and tree-select): add leaf-only prop

* feat(tree and tree-select): add leaf-only prop

* feat(tree and tree-select): add leaf-only prop

* feat(tree and tree-select): add leaf-only prop

* feat(tree and tree-select): add leaf-only prop

* Update src/tree/src/TreeNode.tsx

Co-authored-by: 07akioni <07akioni2@gmail.com>
This commit is contained in:
doom-9 2021-06-29 15:26:09 +08:00 committed by GitHub
parent 684d499e59
commit a379334f00
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 48 additions and 25 deletions

View File

@ -12,6 +12,8 @@
- `n-input` add `input-props` prop.
- `n-message` optimize the error message of `useMessage` when there is no `n-message-provider`, add the related document link.
- Add `web-types.json` for webstorm, however I recommend using VSCode and Volar. `web-types.json` only provides limited information for coding.
- `n-tree-select` add `leaf-only` prop.
- `n-tree` add `leaf-only` prop.
### Fixes

View File

@ -12,6 +12,8 @@
- `n-input` 新增 `input-props` 属性
- `n-message` 优化 `useMessage` 当没有 `n-message-provider` 时的报错信息,增加关联的文档链接
- 为 webstorm 添加 `web-types.json`,但是我还是推荐使用 VSCode 和 Volar`web-types.json` 只能为编码提供很有限的信息
- `n-tree-select` 新增 `leaf-only` 属性
- `n-tree` 新增 `leaf-only` 属性
### Fixes

View File

@ -29,6 +29,7 @@ debug
| expanded-keys | `Array<string \| number>` | `undefined` | 展开节点的 key |
| filterable | `boolean` | `false` | 是否可过滤 |
| filter | `(pattern: string, option: TreeSelectOption) => boolean` | - | 过滤器函数 |
| leaf-only | `boolean` | `false` | 是否开启仅末层树节点可选 |
| max-tag-count | `number \| 'responsive'` | `undefined` | 多选时最多直接显示多少选项,设为 `'responsive'` 会保证最多一行 |
| multiple | `boolean` | `false` | 是否支持多选 |
| options | `TreeSelectOption[]` | `[]` | 选项 |

View File

@ -73,6 +73,7 @@ const props = {
},
disabled: Boolean,
filterable: Boolean,
leafOnly: Boolean,
maxTagCount: [String, Number] as PropType<number | 'responsive'>,
multiple: Boolean,
options: {
@ -661,6 +662,7 @@ export default defineComponent({
selectedKeys={this.treeSelectedKeys}
checkable={checkable}
cascade={this.mergedCascade}
leafOnly={this.leafOnly}
multiple={this.multiple}
virtualScroll={
this.consistentMenuWidth &&

View File

@ -39,6 +39,7 @@ disabled
| expand-on-dragenter | `boolean` | `true` | 是否在拖入后展开节点 |
| expanded-keys | `Array<string \| number>` | `undefined` | 如果设定则展开受控 |
| filter | `(node: TreeOption) => boolean` | 一个简单的字符串过滤算法 | |
| leaf-only | `boolean` | `false` | 是否开启仅末层树节点可选 |
| multiple | `boolean` | `false` | |
| on-load | `(node: TreeOption) => Promise<void>` | `undefined` | |
| pattern | `string` | `''` | |

View File

@ -107,6 +107,7 @@ const treeProps = {
default: () => []
},
remote: Boolean,
leafOnly: Boolean,
multiple: Boolean,
pattern: {
type: String,
@ -494,7 +495,7 @@ export default defineComponent({
nodeKeyToBeExpanded = null
}
function handleCheck (node: TmNode, checked: boolean): void {
if (props.disabled || node.disabled) return
if (props.disabled || node.disabled || (props.leafOnly && !node.isLeaf)) { return }
const { checkedKeys } = dataTreeMateRef.value![
checked ? 'check' : 'uncheck'
](node.key, displayedCheckedKeysRef.value, {
@ -521,7 +522,12 @@ export default defineComponent({
toggleExpand(node.key)
}
function handleSelect (node: TmNode): void {
if (props.disabled || node.disabled || !props.selectable) return
if (
props.disabled ||
node.disabled ||
!props.selectable ||
(props.leafOnly && !node.isLeaf)
) { return }
pendingNodeKeyRef.value = node.key
if (props.internalCheckOnSelect) {
const {
@ -941,10 +947,12 @@ export default defineComponent({
mergedExpandedKeysRef,
mergedThemeRef: themeRef,
disabledRef: toRef(props, 'disabled'),
checkableRef: toRef(props, 'checkable'),
leafOnlyRef: toRef(props, 'leafOnly'),
selectableRef: toRef(props, 'selectable'),
remoteRef: toRef(props, 'remote'),
onLoadRef: toRef(props, 'onLoad'),
draggableRef: toRef(props, 'draggable'),
checkableRef: toRef(props, 'checkable'),
blockLineRef: toRef(props, 'blockLine'),
indentRef: toRef(props, 'indent'),
droppingMouseNodeRef,
@ -970,6 +978,7 @@ export default defineComponent({
handleKeydown,
handleKeyup
}
return {
mergedClsPrefix: mergedClsPrefixRef,
mergedTheme: themeRef,
@ -1024,7 +1033,6 @@ export default defineComponent({
blockNode,
blockLine,
draggable,
selectable,
disabled,
internalFocusable,
handleKeyup,
@ -1036,11 +1044,10 @@ export default defineComponent({
const treeClass = [
`${mergedClsPrefix}-tree`,
(blockLine || blockNode) && `${mergedClsPrefix}-tree--block-node`,
blockLine && `${mergedClsPrefix}-tree--block-line`,
selectable && `${mergedClsPrefix}-tree--selectable`
blockLine && `${mergedClsPrefix}-tree--block-line`
]
const createNode = (tmNode: TmNode | MotionData): VNode =>
'__motion' in tmNode ? (
const createNode = (tmNode: TmNode | MotionData): VNode => {
return '__motion' in tmNode ? (
<MotionWrapper
height={tmNode.height}
nodes={tmNode.nodes}
@ -1055,6 +1062,8 @@ export default defineComponent({
clsPrefix={mergedClsPrefix}
/>
)
}
if (this.virtualScroll) {
const { mergedTheme, internalScrollablePadding } = this
const padding = getPadding(internalScrollablePadding || '0')

View File

@ -46,6 +46,7 @@ const TreeNode = defineComponent({
const contentInstRef = ref<null | ComponentPublicInstance>(null)
// must be non-reactive
const contentElRef: { value: HTMLElement | null } = { value: null }
onMounted(() => {
contentElRef.value = contentInstRef.value!.$el as HTMLElement
})
@ -177,9 +178,18 @@ const TreeNode = defineComponent({
disabled: computed(
() => NTree.disabledRef.value || props.tmNode.disabled
),
checkable: computed(
() =>
NTree.checkableRef.value &&
(NTree.leafOnlyRef.value ? props.tmNode.isLeaf : true)
),
checkboxDisabled: computed(() => !!props.tmNode.rawNode.checkboxDisabled),
selectable: computed(
() =>
NTree.selectableRef.value &&
(NTree.leafOnlyRef.value ? !!props.tmNode.isLeaf : true)
),
internalScrollable: NTree.internalScrollableRef,
checkable: NTree.checkableRef,
draggable: NTree.draggableRef,
blockLine: NTree.blockLineRef,
checkboxFocusable: NTree.internalCheckboxFocusableRef,
@ -204,6 +214,7 @@ const TreeNode = defineComponent({
tmNode,
clsPrefix,
checkable,
selectable,
selected,
highlight,
draggable,
@ -239,7 +250,8 @@ const TreeNode = defineComponent({
[`${clsPrefix}-tree-node--checkable`]: checkable,
[`${clsPrefix}-tree-node--highlight`]: highlight,
[`${clsPrefix}-tree-node--pending`]: pending,
[`${clsPrefix}-tree-node--disabled`]: disabled
[`${clsPrefix}-tree-node--disabled`]: disabled,
[`${clsPrefix}-tree-node--selectable`]: selectable
}
]}
data-key={dataKey}

View File

@ -60,7 +60,6 @@ export interface TreeInjection {
fNodesRef: Ref<Array<TreeNode<TreeOption>>>
remoteRef: Ref<boolean>
draggableRef: Ref<boolean>
checkableRef: Ref<boolean>
mergedThemeRef: Ref<MergedTheme<TreeTheme>>
onLoadRef: Ref<((node: TreeOption) => Promise<void>) | undefined>
blockLineRef: Ref<boolean>
@ -71,6 +70,9 @@ export interface TreeInjection {
droppingPositionRef: Ref<null | DropPosition>
droppingOffsetLevelRef: Ref<number>
disabledRef: Ref<boolean>
checkableRef: Ref<boolean>
leafOnlyRef: Ref<boolean>
selectableRef: Ref<boolean>
pendingNodeKeyRef: Ref<null | Key>
internalScrollableRef: Ref<boolean>
internalCheckboxFocusableRef: Ref<boolean>

View File

@ -74,23 +74,14 @@ export default cB('tree', `
color: var(--node-text-color-disabled);
cursor: not-allowed;
`)
])
]),
cM('selectable', [
cM('block-line', [
cB('tree-node', [
cNotM('disabled', `
cursor: pointer;
`)
])
]),
cB('tree-node', [
cNotM('disabled', [
cNotM('disabled', [
cM('selectable', [
cB('tree-node-content', `
cursor: pointer;
cursor: pointer;
`)
])
])
])]
)
]),
cM('block-node', [
cB('tree-node-content', `
@ -188,6 +179,7 @@ export default cB('tree', `
align-items: center;
vertical-align: bottom;
padding: 0 6px;
cursor: default;
border-radius: var(--node-border-radius);
text-decoration-color: #0000;
text-decoration-line: underline;