Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(pro:transfer): strategy parent with flat-target-data not working #1204

Merged
merged 1 commit into from
Oct 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1448,7 +1448,7 @@ exports[`ProTransfer > tree transfer render work 1`] = `
class="ix-transfer-header-label"
>
待选 (18)
待选 (27)
</span>
<!---->
Expand Down Expand Up @@ -3113,7 +3113,7 @@ exports[`ProTransfer > tree transfer render work 1`] = `
class="ix-transfer-header-label"
>
已选 (2)
已选 (3)
</span>
<!---->
Expand Down
161 changes: 109 additions & 52 deletions packages/pro/transfer/src/composables/useTreeDataStrategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,39 +15,29 @@ import { type ComputedRef, type Ref, ref, watch } from 'vue'
import { type CascaderStrategy } from '@idux/components/cascader'

import { combineTrees, filterTree, flattenTree, genFlattenedTreeKeys, traverseTree } from '../utils'
import { useTreeDataStrategyContext } from './useTreeDataStrategyContext'
import { type TreeDataStrategyContext, useTreeDataStrategyContext } from './useTreeDataStrategyContext'

export function useTreeDataStrategies<C extends VKey>(
props: ProTransferProps,
childrenKey: ComputedRef<C>,
cascadeStrategy: ComputedRef<CascaderStrategy>,
): {
dataKeyMap: Map<VKey, TreeTransferData<C>>
parentKeyMap: Map<VKey, VKey | undefined>
dataStrategy: Ref<TransferDataStrategyProp<TreeTransferData<C>>>
context: TreeDataStrategyContext<C>
mergedDataStrategy: Ref<TransferDataStrategyProp<TreeTransferData<C>>>
} {
const { cachedTargetData, dataKeyMap, parentKeyMap, dataStrategy } = useTreeDataStrategyContext(props, childrenKey)

const context = useTreeDataStrategyContext(props, childrenKey)
const getMergedDataStrategy = () => ({
...dataStrategy,
...createStrategy(
childrenKey,
cachedTargetData,
parentKeyMap,
dataKeyMap,
cascadeStrategy.value,
props.flatTargetData,
),
...context.baseDataStrategy,
...createStrategy(context, childrenKey, cascadeStrategy.value, props.flatTargetData),
})
const mergedDataStrategy = ref<TransferDataStrategyProp<TreeTransferData<C>>>(getMergedDataStrategy())
watch([cascadeStrategy, childrenKey], () => {
mergedDataStrategy.value = getMergedDataStrategy()
})

return {
dataKeyMap,
parentKeyMap,
dataStrategy: mergedDataStrategy,
context,
mergedDataStrategy,
}
}

Expand Down Expand Up @@ -122,8 +112,10 @@ function createSeparateDataSourceFn<C extends VKey>(
childrenKey: ComputedRef<C>,
cachedTargetData: Ref<TreeTransferData<C>[]>,
cascaderStrategy: CascaderStrategy,
flatTargetData: boolean | 'all' = false,
targetDataCount: Ref<number>,
): Exclude<TransferDataStrategyProp<TreeTransferData<C>>['separateDataSource'], undefined> {
const targetDataKeySet = new Set<VKey>()

const getFilterFn = (
selectedKeySet: Set<VKey>,
getKey: GetKeyFn,
Expand All @@ -144,18 +136,23 @@ function createSeparateDataSourceFn<C extends VKey>(
}
})()

return (data, isSource) => {
if (isSource || (cascaderStrategy !== 'off' && !flatTargetData)) {
return filterTree(data, childrenKey.value, (item, parent) => filterFn(item, parent, isSource), 'or')
}

return flattenTree(
return (data, isSource) =>
filterTree(
data,
childrenKey.value,
item => ({ ...item, children: undefined }),
flatTargetData !== 'all' && cascaderStrategy !== 'off',
).filter(item => selectedKeySet.has(getKey(item)))
}
(item, parent) => {
const filterRes = filterFn(item, parent, isSource)

// set targetDataKeySet to collect targetData count later
// we collect this during filter process to avoid unecessary traveral
if (!isSource && filterRes) {
targetDataKeySet.add(getKey(item))
}

return filterRes
},
'or',
)
}

return (data, _, selectedKeySet, getKey) => {
Expand All @@ -164,6 +161,9 @@ function createSeparateDataSourceFn<C extends VKey>(
const newTargetData = filterData(data, false)
const previousTargetData = filterData(cachedTargetData.value, false)

targetDataCount.value = targetDataKeySet.size
targetDataKeySet.clear()

// combine new data with previous data
// beacause we intend to cache selected data after dataSource changes
const targetData = combineTrees(newTargetData, previousTargetData, childrenKey.value, getKey)
Expand All @@ -176,48 +176,90 @@ function createSeparateDataSourceFn<C extends VKey>(
}
}

function createStrategy<C extends VKey>(
function createSeparateDataSourceFnWithFlatten<C extends VKey>(
childrenKey: ComputedRef<C>,
cachedTargetData: Ref<TreeTransferData<C>[]>,
parentKeyMap: Map<VKey, VKey | undefined>,
dataKeyMap: Map<VKey, TreeTransferData<C>>,
cascaderStrategy: CascaderStrategy,
targetDataCount: Ref<number>,
flatTargetData: boolean | 'all' = false,
): Exclude<TransferDataStrategyProp<TreeTransferData<C>>['separateDataSource'], undefined> {
const fn = createSeparateDataSourceFn(childrenKey, cachedTargetData, cascaderStrategy, targetDataCount)

return (...args) => {
const { sourceData, targetData } = fn(...args)

return {
sourceData,
targetData: flattenTargetTree(targetData, childrenKey.value, cascaderStrategy, flatTargetData),
}
}
}

function flattenTargetTree<C extends VKey>(
data: TreeTransferData<C>[],
childrenKey: C,
cascaderStrategy: CascaderStrategy,
flatTargetData: boolean | 'all',
) {
if (cascaderStrategy !== 'off' && !flatTargetData) {
return data
}

return flattenTree(
data,
childrenKey,
item => ({ ...item, children: undefined }),
flatTargetData !== 'all' && cascaderStrategy !== 'off',
)
}

function createStrategy<C extends VKey>(
context: TreeDataStrategyContext<C>,
childrenKey: ComputedRef<C>,
cascaderStrategy: CascaderStrategy,
flatTargetData: boolean | 'all',
) {
switch (cascaderStrategy) {
case 'parent':
return createParentStrategy(childrenKey, cachedTargetData, dataKeyMap, flatTargetData)
return createParentStrategy(context, childrenKey, flatTargetData)
case 'child':
return createChildStrategy(childrenKey, cachedTargetData, parentKeyMap, flatTargetData)
return createChildStrategy(context, childrenKey, flatTargetData)
case 'off':
return createOffStrategy(childrenKey, cachedTargetData, flatTargetData)
return createOffStrategy(context, childrenKey, flatTargetData)
case 'all':
default:
return createAllStrategy(childrenKey, cachedTargetData, parentKeyMap, flatTargetData)
return createAllStrategy(context, childrenKey, flatTargetData)
}
}

function createAllStrategy<C extends VKey>(
context: TreeDataStrategyContext<C>,
childrenKey: ComputedRef<C>,
cachedTargetData: Ref<TreeTransferData<C>[]>,
parentKeyMap: Map<VKey, VKey | undefined>,
flatTargetData: boolean | 'all',
): TransferDataStrategyProp<TreeTransferData<C>> {
const { cachedTargetData, parentKeyMap, targetDataCount } = context
return {
genDisabledKeys: (data, getKey) => genDisabledKeys(data, getKey, childrenKey.value, true),
separateDataSource: createSeparateDataSourceFn(childrenKey, cachedTargetData, 'all', flatTargetData),
separateDataSource: createSeparateDataSourceFnWithFlatten(
childrenKey,
cachedTargetData,
'all',
targetDataCount,
flatTargetData,
),
getAllSelectedKeys: (selected, data, selectedKeySet, disabledKeySet, getKey) =>
getAllSelectedKeys(selected, data, selectedKeySet, disabledKeySet, getKey, childrenKey.value),
remove: (keys, selectedKeySet) => commonRemoveFn(keys, selectedKeySet, parentKeyMap),
}
}

function createChildStrategy<C extends VKey>(
context: TreeDataStrategyContext<C>,
childrenKey: ComputedRef<C>,
cachedTargetData: Ref<TreeTransferData<C>[]>,
parentKeyMap: Map<VKey, VKey | undefined>,
flatTargetData: boolean | 'all',
): TransferDataStrategyProp<TreeTransferData<C>> {
const { cachedTargetData, parentKeyMap, targetDataCount } = context

return {
genDisabledKeys: (data, getKey) => genDisabledKeys(data, getKey, childrenKey.value, true),
getAllSelectedKeys(selected, data, selectedKeySet, disabledKeySet, getKey) {
Expand All @@ -237,19 +279,24 @@ function createChildStrategy<C extends VKey>(

return keys
},
separateDataSource: createSeparateDataSourceFn(childrenKey, cachedTargetData, 'child', flatTargetData),
separateDataSource: createSeparateDataSourceFnWithFlatten(
childrenKey,
cachedTargetData,
'child',
targetDataCount,
flatTargetData,
),
remove: (keys, selectedKeySet) => commonRemoveFn(keys, selectedKeySet, parentKeyMap),
}
}

function createParentStrategy<C extends VKey>(
context: TreeDataStrategyContext<C>,
childrenKey: ComputedRef<C>,
cachedTargetData: Ref<TreeTransferData<C>[]>,
dataKeyMap: Map<VKey, TreeTransferData<C>>,
flatTargetData: boolean | 'all',
): TransferDataStrategyProp<TreeTransferData<C>> {
const separateDataSource = createSeparateDataSourceFn(childrenKey, cachedTargetData, 'parent', flatTargetData)
let targetData: TreeTransferData<C>[] = []
const { cachedTargetData, dataKeyMap, targetDataCount } = context
const separateDataSource = createSeparateDataSourceFn(childrenKey, cachedTargetData, 'parent', targetDataCount)

return {
genDisabledKeys: (data, getKey) => genDisabledKeys(data, getKey, childrenKey.value, true),
Expand Down Expand Up @@ -287,9 +334,11 @@ function createParentStrategy<C extends VKey>(
return keys
},
separateDataSource(data, _, selectedKeySet, getKey) {
const separatedData = separateDataSource(data, _, selectedKeySet, getKey)
targetData = separatedData.targetData
return separatedData
const { sourceData, targetData } = separateDataSource(data, _, selectedKeySet, getKey)
return {
sourceData,
targetData: flattenTargetTree(targetData, childrenKey.value, 'parent', flatTargetData),
}
},
append(keys, selectedKey, getKey) {
const newKeySet = new Set([...selectedKey, ...keys])
Expand Down Expand Up @@ -317,7 +366,7 @@ function createParentStrategy<C extends VKey>(
}

// it is not possible to get the correct selected keys from bottom to top
traverseTree(targetData, childrenKey.value, (item, parents) => {
traverseTree(cachedTargetData.value, childrenKey.value, (item, parents) => {
if (keySet.has(getKey(item))) {
const keysInChain = [item, ...parents].map(getKey)
const selectedKeyIdx = keysInChain.findIndex(key => newKeySet.has(key))
Expand Down Expand Up @@ -351,13 +400,21 @@ function createParentStrategy<C extends VKey>(
}

function createOffStrategy<C extends VKey>(
context: TreeDataStrategyContext<C>,
childrenKey: ComputedRef<C>,
cachedTargetData: Ref<TreeTransferData<C>[]>,
flatTargetData: boolean | 'all',
): TransferDataStrategyProp<TreeTransferData<C>> {
const { cachedTargetData, targetDataCount } = context

return {
genDisabledKeys: (data, getKey) => genDisabledKeys(data, getKey, childrenKey.value, false),
separateDataSource: createSeparateDataSourceFn(childrenKey, cachedTargetData, 'off', flatTargetData),
separateDataSource: createSeparateDataSourceFnWithFlatten(
childrenKey,
cachedTargetData,
'off',
targetDataCount,
flatTargetData,
),
getAllSelectedKeys: (selected, data, selectedKeySet, disabledKeySet, getKey) =>
getAllSelectedKeys(selected, data, selectedKeySet, disabledKeySet, getKey, childrenKey.value),
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,20 @@ import { type ComputedRef, type Ref, onUnmounted, ref } from 'vue'

import { filterTree, genFlattenedTreeKeys, traverseTree } from '../utils'

export function useTreeDataStrategyContext<C extends VKey>(
props: ProTransferProps,
childrenKey: ComputedRef<C>,
): {
export interface TreeDataStrategyContext<C extends VKey> {
baseDataStrategy: TransferDataStrategyProp<TreeTransferData<C>>
dataKeyMap: Map<VKey, TreeTransferData<C>>
parentKeyMap: Map<VKey, VKey | undefined>
cachedTargetData: Ref<TreeTransferData<C>[]>
dataStrategy: TransferDataStrategyProp<TreeTransferData<C>>
} {
targetDataCount: Ref<number>
}

export function useTreeDataStrategyContext<C extends VKey>(
props: ProTransferProps,
childrenKey: ComputedRef<C>,
): TreeDataStrategyContext<C> {
const cachedTargetData = ref(props.defaultTargetData ?? []) as Ref<TreeTransferData<C>[]>
const targetDataCount = ref(0)
const dataKeyMap: Map<VKey, TreeTransferData<C>> = new Map()
const parentKeyMap: Map<VKey, VKey | undefined> = new Map()

Expand All @@ -33,10 +37,11 @@ export function useTreeDataStrategyContext<C extends VKey>(
})

return {
cachedTargetData,
targetDataCount,
dataKeyMap,
parentKeyMap,
cachedTargetData,
dataStrategy: {
baseDataStrategy: {
genDataKeys: (data, getKey) => {
return new Set(genFlattenedTreeKeys(data, childrenKey.value, getKey))
},
Expand Down
Loading