feat(tree): support accrodion animation

This commit is contained in:
07akioni 2022-07-05 00:39:22 +08:00
parent acfdca7cde
commit 50adfe99a2
3 changed files with 31 additions and 21 deletions

View File

@ -29,7 +29,7 @@ checkbox-placement.vue
| Name | Type | default | Description | Version | | Name | Type | default | Description | Version |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| accordion | `boolean` | `false` | Whether to use accrodion expand mode. Please note that it will make expand animation not working in some circumstances. We may fix it later, but if you want to use the prop, you should know it. | NEXT_VERSION | | accordion | `boolean` | `false` | Whether to use accrodion expand mode. | NEXT_VERSION |
| allow-checking-not-loaded | `boolean` | `false` | Whether to allow cascade checking on not loaded nodes. If you want to use this, you should know the `check-keys` may be incomplete. Also, you should aware about the consistency bewteen naive's checking logic and your backend's checking logic, especially when there are disabled nodes. | 2.28.1 | | allow-checking-not-loaded | `boolean` | `false` | Whether to allow cascade checking on not loaded nodes. If you want to use this, you should know the `check-keys` may be incomplete. Also, you should aware about the consistency bewteen naive's checking logic and your backend's checking logic, especially when there are disabled nodes. | 2.28.1 |
| allow-drop | `(info: { dropPosition: DropPosition, node: TreeOption, phase: 'drag' \| 'drop' }) => boolean` | A function that prohibit dropping inside leaf node. | Whether to allow dropping. | | | allow-drop | `(info: { dropPosition: DropPosition, node: TreeOption, phase: 'drag' \| 'drop' }) => boolean` | A function that prohibit dropping inside leaf node. | Whether to allow dropping. | |
| block-line | `boolean` | `false` | Nodes spread out the whole row. | | | block-line | `boolean` | `false` | Nodes spread out the whole row. | |
@ -41,7 +41,7 @@ checkbox-placement.vue
| checkbox-placement | `'left' \| 'right'` | `'left'` | Checkbox's placement. | 2.28.3 | | checkbox-placement | `'left' \| 'right'` | `'left'` | Checkbox's placement. | 2.28.3 |
| children-field | `string` | `'children'` | The children field in `TreeOption`. | | | children-field | `string` | `'children'` | The children field in `TreeOption`. | |
| checked-keys | `Array<string \| number>` | `undefined` | Checked keys of the tree. | | | checked-keys | `Array<string \| number>` | `undefined` | Checked keys of the tree. | |
| check-on-click | `boolean \| ((node: TreeOption) => boolean)` | `true` | Allow node clicking to trigger check when `checkable` is `true` | NEXT_VERSION | | check-on-click | `boolean \| ((node: TreeOption) => boolean)` | `false` | Allow node clicking to trigger check when `checkable` is `true` | NEXT_VERSION |
| data | `Array<TreeOption>` | `[]` | The node data of the tree. Reset `data` will cause clearing of some uncontrolled status. If you need to modify data, you'd better make tree work in a controlled manner. | | | data | `Array<TreeOption>` | `[]` | The node data of the tree. Reset `data` will cause clearing of some uncontrolled status. If you need to modify data, you'd better make tree work in a controlled manner. | |
| default-checked-keys | `Array<string \| number>` | `[]` | Multiple options selected by default. | | | default-checked-keys | `Array<string \| number>` | `[]` | Multiple options selected by default. | |
| default-expand-all | `boolean` | `false` | Expand all options. | | | default-expand-all | `boolean` | `false` | Expand all options. | |

View File

@ -33,7 +33,7 @@ scroll-debug.vue
| 名称 | 类型 | 默认值 | 说明 | 版本 | | 名称 | 类型 | 默认值 | 说明 | 版本 |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| accordion | `boolean` | `false` | 是否使用手风琴展开模式。需要注意的是这个属性会导致某些情况下展开动画的丢失,我们未来可能会修复这个问题,但是如果你要使用这个属性,请了解这一点 | NEXT_VERSION | | accordion | `boolean` | `false` | 是否使用手风琴展开模式 | NEXT_VERSION |
| allow-checking-not-loaded | `boolean` | `false` | 是否允许级联勾选还没有完全加载的节点。如果你要用这个属性,请记住 `checked-keys` 可能是不完整的,并且请注意勾选行为和后端计算逻辑的一致性,尤其是有禁用节点的情况下 | 2.28.1 | | allow-checking-not-loaded | `boolean` | `false` | 是否允许级联勾选还没有完全加载的节点。如果你要用这个属性,请记住 `checked-keys` 可能是不完整的,并且请注意勾选行为和后端计算逻辑的一致性,尤其是有禁用节点的情况下 | 2.28.1 |
| allow-drop | `(info: { dropPosition: DropPosition, node: TreeOption, phase: 'drag' \| 'drop' }) => boolean` | 一个不允许 drop 在叶节点内部的函数 | 是否允许 drop | | | allow-drop | `(info: { dropPosition: DropPosition, node: TreeOption, phase: 'drag' \| 'drop' }) => boolean` | 一个不允许 drop 在叶节点内部的函数 | 是否允许 drop | |
| block-line | `boolean` | `false` | 节点整行撑开 | | | block-line | `boolean` | `false` | 节点整行撑开 | |
@ -45,7 +45,7 @@ scroll-debug.vue
| checkbox-placement | `'left' \| 'right'` | `'left'` | 复选框的位置 | 2.28.3 | | checkbox-placement | `'left' \| 'right'` | `'left'` | 复选框的位置 | 2.28.3 |
| children-field | `string` | `'children'` | 替代 `TreeOption` 中的 children 字段名 | | | children-field | `string` | `'children'` | 替代 `TreeOption` 中的 children 字段名 | |
| checked-keys | `Array<string \| number>` | `undefined` | 如果设定则 `checked` 状态受控 | | | checked-keys | `Array<string \| number>` | `undefined` | 如果设定则 `checked` 状态受控 | |
| check-on-click | `boolean \| ((node: TreeOption) => boolean)` | `true` | 是否允许点击节点进行勾选,仅在 `checkable``true` 时生效 | NEXT_VERSION | | check-on-click | `boolean \| ((node: TreeOption) => boolean)` | `false` | 是否允许点击节点进行勾选,仅在 `checkable``true` 时生效 | NEXT_VERSION |
| data | `Array<TreeOption>` | `[]` | 树的节点数据。重新设置 `data` 会将一些非受控状态清空,如果你需要在使用中改动 `data`,最好以受控的方式控制树 | | | data | `Array<TreeOption>` | `[]` | 树的节点数据。重新设置 `data` 会将一些非受控状态清空,如果你需要在使用中改动 `data`,最好以受控的方式控制树 | |
| default-checked-keys | `Array<string \| number>` | `[]` | 默认选中的多选项 | | | default-checked-keys | `Array<string \| number>` | `[]` | 默认选中的多选项 | |
| default-expand-all | `boolean` | `false` | 展开全部选项 | | | default-expand-all | `boolean` | `false` | 展开全部选项 | |

View File

@ -146,7 +146,7 @@ export const treeProps = {
expandOnClick: Boolean, expandOnClick: Boolean,
checkOnClick: { checkOnClick: {
type: [Boolean, Function] as PropType<boolean | CheckOnClick>, type: [Boolean, Function] as PropType<boolean | CheckOnClick>,
default: true default: false
}, },
cancelable: { cancelable: {
type: Boolean, type: Boolean,
@ -557,7 +557,7 @@ export default defineComponent({
// animation in progress // animation in progress
const aipRef = ref(false) const aipRef = ref(false)
// animation flattened nodes // animation flattened nodes
const afNodeRef = ref<Array<TmNode | MotionData>>([]) const afNodesRef = ref<Array<TmNode | MotionData>>([])
// Note: Since the virtual list depends on min height, if there's a node // Note: Since the virtual list depends on min height, if there's a node
// whose height starts from 0, the virtual list will have a wrong height // whose height starts from 0, the virtual list will have a wrong height
// during animation. This will seldom cause wired scrollbar status. It is // during animation. This will seldom cause wired scrollbar status. It is
@ -584,10 +584,7 @@ export default defineComponent({
removedKey = expandedKey removedKey = expandedKey
} }
} }
if ( if (addedKey === null && removedKey === null) {
(addedKey !== null && removedKey !== null) ||
(addedKey === null && removedKey === null)
) {
// 1. multi action, not triggered by click // 1. multi action, not triggered by click
// 2. no action, don't know what happened // 2. no action, don't know what happened
return return
@ -597,20 +594,34 @@ export default defineComponent({
virtualScroll ? virtualListInstRef.value!.listElRef : selfElRef.value! virtualScroll ? virtualListInstRef.value!.listElRef : selfElRef.value!
).offsetHeight ).offsetHeight
const viewportItemCount = Math.ceil(viewportHeight / ITEM_SIZE) + 1 const viewportItemCount = Math.ceil(viewportHeight / ITEM_SIZE) + 1
// play add animation
let baseExpandedKeys: Key[] | undefined
if (addedKey !== null) { if (addedKey !== null) {
// play add animation baseExpandedKeys = prevValue
aipRef.value = true }
afNodeRef.value = displayTreeMateRef.value.getFlattenedNodes(prevValue) if (removedKey !== null) {
const expandedNodeIndex = afNodeRef.value.findIndex( if (baseExpandedKeys === undefined) {
baseExpandedKeys = value
} else {
baseExpandedKeys = baseExpandedKeys.filter(
(key) => key !== removedKey
)
}
}
aipRef.value = true
afNodesRef.value =
displayTreeMateRef.value.getFlattenedNodes(baseExpandedKeys)
if (addedKey !== null) {
const expandedNodeIndex = afNodesRef.value.findIndex(
(node) => (node as any).key === addedKey (node) => (node as any).key === addedKey
) )
if (~expandedNodeIndex) { if (~expandedNodeIndex) {
const expandedChildren = flatten( const expandedChildren = flatten(
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
(afNodeRef.value[expandedNodeIndex] as TmNode).children!, (afNodesRef.value[expandedNodeIndex] as TmNode).children!,
value value
) )
afNodeRef.value.splice(expandedNodeIndex + 1, 0, { afNodesRef.value.splice(expandedNodeIndex + 1, 0, {
__motion: true, __motion: true,
mode: 'expand', mode: 'expand',
height: virtualScroll height: virtualScroll
@ -623,13 +634,12 @@ export default defineComponent({
} }
} }
if (removedKey !== null) { if (removedKey !== null) {
afNodeRef.value = displayTreeMateRef.value.getFlattenedNodes(value) const collapsedNodeIndex = afNodesRef.value.findIndex(
const collapsedNodeIndex = afNodeRef.value.findIndex(
(node) => (node as any).key === removedKey (node) => (node as any).key === removedKey
) )
if (~collapsedNodeIndex) { if (~collapsedNodeIndex) {
const collapsedNodeChildren = ( const collapsedNodeChildren = (
afNodeRef.value[collapsedNodeIndex] as TmNode afNodesRef.value[collapsedNodeIndex] as TmNode
).children ).children
// Sometime the whole tree is change, remove a key doesn't mean it is collapsed, // Sometime the whole tree is change, remove a key doesn't mean it is collapsed,
// but maybe children removed // but maybe children removed
@ -637,7 +647,7 @@ export default defineComponent({
// play remove animation // play remove animation
aipRef.value = true aipRef.value = true
const collapsedChildren = flatten(collapsedNodeChildren, value) const collapsedChildren = flatten(collapsedNodeChildren, value)
afNodeRef.value.splice(collapsedNodeIndex + 1, 0, { afNodesRef.value.splice(collapsedNodeIndex + 1, 0, {
__motion: true, __motion: true,
mode: 'collapse', mode: 'collapse',
height: virtualScroll height: virtualScroll
@ -656,7 +666,7 @@ export default defineComponent({
}) })
const mergedFNodesRef = computed(() => { const mergedFNodesRef = computed(() => {
if (aipRef.value) return afNodeRef.value if (aipRef.value) return afNodesRef.value
else return fNodesRef.value else return fNodesRef.value
}) })