Skip to content

Commit

Permalink
feat(pro:transfer): add pro transfer compoennt (#815)
Browse files Browse the repository at this point in the history
add table and tree transfer

feat(comp:tree): modify tree disabled logic
node disabled property and disabled function is now combined

fix(comp:transfer): fix transfer select disabled status
selection is not disabled now when the item is disabled
  • Loading branch information
sallerli1 committed Mar 25, 2022
1 parent df82e5d commit e367009
Show file tree
Hide file tree
Showing 70 changed files with 6,144 additions and 127 deletions.
4 changes: 2 additions & 2 deletions packages/components/transfer/__tests__/transfer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -377,10 +377,10 @@ describe('Transfer', () => {
expect(targetList.findAllComponents(CheckableListItem).length).toBe(1)
})

test('transferBySelect work', async () => {
test('immediate work', async () => {
const onChange = jest.fn()
const wrapper = TransferMount({
props: { dataSource: mockedDataSource, mode: 'transferBySelect', 'onUpdate:value': onChange },
props: { dataSource: mockedDataSource, mode: 'immediate', 'onUpdate:value': onChange },
})

const [sourceList, targetList] = wrapper.findAllComponents(TransferList)
Expand Down
4 changes: 2 additions & 2 deletions packages/components/transfer/__tests__/transferSlots.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@ describe('Transfer', () => {

selectedKeys = [7, 8, 9, 10, 11, 12]
await wrapper.find('.source-handleSelectChange').trigger('click')
expect(wrapper.find('.source-selectedKeys').text()).toBe('5')
expect(wrapper.find('.source-selectedKeySet').text()).toBe('5')
expect(wrapper.find('.source-selectedKeys').text()).toBe('6')
expect(wrapper.find('.source-selectedKeySet').text()).toBe('6')

await wrapper.find('.triggerAppend').trigger('click')
expect(wrapper.find('.source-selectedKeys').text()).toBe('0')
Expand Down
2 changes: 1 addition & 1 deletion packages/components/transfer/demo/NoOperations.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<IxSpace vertical>
<IxTransfer v-model:value="selectedKeys" :data-source="dataSource" :disabled="disabled" mode="transferBySelect" />
<IxTransfer v-model:value="selectedKeys" :data-source="dataSource" :disabled="disabled" mode="immediate" />
<IxSpace align="center">
<label>disabled:</label>
<IxSwitch v-model:checked="disabled" />
Expand Down
4 changes: 2 additions & 2 deletions packages/components/transfer/demo/Pagination.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
</template>

<script setup lang="ts">
import type { TransferPaginationType } from '@idux/components/transfer'
import type { TransferPaginationProps } from '@idux/components/transfer'
import { computed, ref } from 'vue'
Expand All @@ -24,7 +24,7 @@ const dataSource: Data[] = Array.from(new Array(100)).map((_, idx) => ({
const sourcePageIndex = ref(1)
const targetPageIndex = ref(1)
const pagination = computed<TransferPaginationType>(() => ({
const pagination = computed<TransferPaginationProps>(() => ({
pageIndex: [sourcePageIndex.value, targetPageIndex.value],
pageSize: [10, 10],
onChange(isSource, pageIndex) {
Expand Down
6 changes: 3 additions & 3 deletions packages/components/transfer/demo/Remote.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
:pagination="pagination"
:spin="spin"
:searchable="{ source: true, target: false }"
mode="transferBySelect"
mode="immediate"
:on-search="handleSearchChange"
>
<template #label="item">{{ item.label ?? 'Option' + item.key }}</template>
</IxTransfer>
</template>

<script setup lang="ts">
import type { TransferPaginationType } from '@idux/components/transfer'
import type { TransferPaginationProps } from '@idux/components/transfer'

import { computed, onMounted, ref } from 'vue'

Expand All @@ -39,7 +39,7 @@ const dataSource = ref<Data[]>([])
const filteredData = ref<Data[]>([])

const sourcePageIndex = ref(1)
const pagination = computed<TransferPaginationType>(() => ({
const pagination = computed<TransferPaginationProps>(() => ({
pageIndex: sourcePageIndex.value,
pageSize: [20, 20],
total: filteredData.value.length,
Expand Down
4 changes: 2 additions & 2 deletions packages/components/transfer/demo/VirtualScroll.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ const sourceScrollToIdx = ref(0)
const targetScrollToIdx = ref(0)
const handleSourceScrollTo = () => {
transferRef.value?.scrollTo({ index: sourceScrollToIdx.value }, true)
transferRef.value?.scrollTo(true, { index: sourceScrollToIdx.value })
}
const handleTargetScrollTo = () => {
transferRef.value?.scrollTo({ index: targetScrollToIdx.value }, false)
transferRef.value?.scrollTo(false, { index: targetScrollToIdx.value })
}
</script>
35 changes: 18 additions & 17 deletions packages/components/transfer/docs/Index.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,27 +33,27 @@ export interface TransferData extends Record<VKey, any> {

| 名称 | 说明 | 类型 | 默认值 | 全局配置 | 备注 |
| --- | --- | --- | --- | --- | --- |
| `dataSource` | 源数据数组 | `TransferData[]` | `[]` | - | - |
| `v-model:value` | 已选数据数组 | `VKey[]` | - | - | - |
| `v-model:sourceSelectedKeys` | 源数据列表勾选的keys | `VKey[]` | - | - | - |
| `v-model:targetSelectedKeys` | 目标数据列表勾选的keys | `VKey[]` | - | - | - |
| `clearable` | 是否可清除 | `boolean` | `true` || - |
| `clearIcon` | 清除图标 | `string \| #clearIcon` | `clear` || - |
| `dataSource` | 源数据数组 | `TransferData[]` | `[]` | - | - |
| `disabled` | 是否禁用穿梭框 | `boolean` | `false` | - | - |
| `empty` | 空状态的配置 | `string \| EmptyProps` | - | - | - |
| `getKey` | 数据项 `key` 的取值 | `string \| (item: unknown) => string \| number` | - | - | 默认取数据的 `key` 属性 |
| `virtual` | 是否开启虚拟滚动 | `boolean` | `false` | - | 需要设置 `scroll.height` |
| `scroll` | 是否开启虚拟滚动 | `TransferScroll` | - | 仅使用默认列表并开启 `virtual` 下可用 |
| `searchable` | 数据列表是否可搜索 | `boolean \| { source: boolean, target: boolean }` | `false` || 仅使用默认列表头部时可用 |
| `mode` | 穿梭框模式 | `'default' \| 'immediate'` | `'default'` | - | `'immediate'` 模式为勾选即触发穿梭,不展示穿梭操作按钮 |
| `pagination` | 数据列表分页配置 | `boolean \| TransferPaginationProps` | `false` || 仅使用默认列表底部时可用 |
| `scroll` | 穿梭框列表滚动配置项,可以指定滚动区域的宽、高 | `TransferScroll` | - | - |
| `searchable` | 数据列表是否可搜索 | `boolean \| { source: boolean, target: boolean }` | `false` || - |
| `searchFn` | 搜索的判断函数 | `SearchFn` | - | - |
| `pagination` | 数据列表分页配置 | `boolean \| TransferPaginationType` | `false` || 仅使用默认列表底部时可用 |
| `transferBySelect` | 是否通过源数据的勾选触发穿梭 | `boolean` | `false` || 开启后默认不展示操作按钮 |
| `showSelectAll` | 是否展示全选框 | `boolean` | `true` || - |
| `spin` | 数据列表的加载状态 | `boolean \| { source: boolean, target: boolean }` | `false` | - | - |
| `empty` | 空状态的配置 | `string \| EmptyProps` | - | - | - |
| `clearable` | 是否可清除 | `boolean` | `true` || - |
| `clearIcon` | 清除图标 | `string \| #clearIcon` | `clear` || - |
| `virtual` | 是否开启虚拟滚动 | `boolean` | `false` | - | 需要设置 `scroll.height` |
| `onChange` | 已选数据改变回调函数 | `(keys: VKey[], oldKeys: Vkey[]) => void` | - | - | - |
| `onScroll` | 数据列表滚动事件 | `(isSource: boolean, evt: Event) => void` | - | - | 仅使用默认列表并开启 `virtual` 下可用 |
| `onScrolledChange` | 数据列表滚动的位置发生变化 | `(isSource: boolean, startIndex: number, endIndex: number, visibleNodes: unknown[]) => void` | - | - | 仅使用默认列表并开启 `virtual` 下可用 |
| `onScrolledBottom` | 源数据列表滚动到底部时触发 | `(isSource: boolean) => void` | - | - | 仅使用默认列表并开启 `virtual` 下可用 |
| `onScrolledBottom` | 数据列表滚动到底部时触发 | `(isSource: boolean) => void` | - | - | 仅使用默认列表并开启 `virtual` 下可用 |
| `onSearch` | 穿梭框搜索触发回调函数 | `(isSource: boolean, searchValue: string \| undefined) => void` | - | - | - |
| `onSelectAll` | 数据列表全部勾选回调函数 | `(isSource: boolean, checked: boolean) => void` | - | - | - |
| `onClear` | 已选数据清除的回调函数 | `(isSource: boolean) => void` | - | - | - |
Expand All @@ -71,7 +71,7 @@ export type SearchFn<T extends BaseTransferData = TransferData> = (
searchValue: string,
) => boolean

export interface TransferPaginationType {
export interface TransferPaginationProps {
pageIndex?: [number | undefined, number | undefined] | [number | undefined] | number
pageSize?: [number | undefined, number | undefined] | [number | undefined] | number
disabled?: boolean
Expand All @@ -84,13 +84,14 @@ export interface TransferPaginationType {

| 名称 | 说明 | 参数类型 | 备注 |
| --- | --- | --- | --- |
| `clearIcon` | 清除按钮 | - | - |
| `default` | 穿梭框列表主体 | `TransferBindings & { isSource: boolean }` | - |
| `headerLabel` | 穿梭框列表头部 | `{ data: TransferData[], isSource: boolean }` | - |
| `headerSuffix` | 穿梭框列表头部 | `{ isSource: boolean }` | - |
| `empty` | 穿梭框列表空状态 | `EmptyProps` | 仅在使用默认列表时生效 |
| `footer` | 穿梭框列表底部 | `TransferBindings & { isSource: boolean }` | - |
| `operations` | 穿梭框列表底部 | `TransferOperationsContext` | - |
| `headerLabel` | 穿梭框列表头部标签 | `{ data: TransferData[], isSource: boolean }` | - |
| `headerSuffix` | 穿梭框列表头部后缀 | `{ isSource: boolean }` | - |
| `label` | 穿梭框列表label | `TransferData` | 仅在使用默认列表时生效 |
| `empty` | 穿梭框列表空状态 | `EmptyProps` | 仅在使用默认列表时生效 |
| `operations` | 穿梭框操作按钮区域 | `TransferOperationsContext` | - |

```ts
export interface TransferListSlotParams<T extends TransferData = TransferData> {
Expand Down Expand Up @@ -175,7 +176,7 @@ export interface TransferOperationsSlotParams {

| 名称 | 说明 | 参数类型 | 备注 |
| --- | --- | --- | --- |
| `scrollTo` | 源数据滚动到指定位置 | `(option?: number \| VirtualScrollToOptions, isSource?: boolean) => void` | 仅使用默认列表并开启 `virtual` 下可用 |
| `scrollTo` | 滚动到指定位置 | `(isSource: boolean, option?: number \| VirtualScrollToOptions) => void` | 仅在开启 `virtual` 时可用 |

<!--- insert less variable begin --->
## 主题变量
Expand Down Expand Up @@ -204,4 +205,4 @@ export interface TransferOperationsSlotParams {
| `@transfer-icon-color` | `@color-graphite-d20` | - | - |
| `@transfer-icon-hover-color` | `@color-primary` | - | - |
| `@transfer-icon-active-color` | `@color-primary` | - | - |
<!--- insert less variable end --->
<!--- insert less variable end --->
3 changes: 1 addition & 2 deletions packages/components/transfer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,11 @@ export type {
TransferListSlotParams,
TransferOperationsSlotParams,
TransferData,
TransferPaginationType,
TransferPaginationProps,
TransferBindings,
TransferDataStrategiesConfig,
TransferMode,
TransferScroll,
TransferScrollTo,
SearchFn,
} from './src/types'

Expand Down
21 changes: 3 additions & 18 deletions packages/components/transfer/src/Transfer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import type { ɵCheckableListInstance } from '@idux/components/_private/checkabl

import { computed, defineComponent, provide, ref } from 'vue'

import { isBoolean, isNil } from 'lodash-es'

import { useGlobalConfig } from '@idux/components/config'

import TransferOperations from './TransferOperations'
Expand All @@ -23,7 +21,7 @@ import { useTransferOperations } from './composables/useTransferOperations'
import { useTransferSelectState } from './composables/useTransferSelectState'
import TransferList from './list/TransferList'
import { TRANSFER_OPERATIONS_TOKEN, TRANSFER_SOURCE_TOKEN, TRANSFER_TARGET_TOKEN, transferContext } from './token'
import { type TransferApis, type TransferScrollTo, transferProps } from './types'
import { type TransferApis, transferProps } from './types'

export default defineComponent({
name: 'IxTransfer',
Expand Down Expand Up @@ -57,21 +55,8 @@ export default defineComponent({
const targetCheckableListRef = ref<ɵCheckableListInstance>()

const transferApi: TransferApis = {
scrollTo: ((options, isSource) => {
if (isNil(options) && isNil(isSource)) {
return sourceCheckableListRef.value?.scrollTo()
}

if (isBoolean(options)) {
return (options ? sourceCheckableListRef : targetCheckableListRef).value?.scrollTo()
}

if (isNil(isSource)) {
return sourceCheckableListRef.value?.scrollTo(options)
}

return (isSource ? sourceCheckableListRef : targetCheckableListRef).value?.scrollTo(options)
}) as TransferScrollTo,
scrollTo: (isSource, ...params) =>
(isSource ? sourceCheckableListRef : targetCheckableListRef).value?.scrollTo(...params),
}

expose(transferApi)
Expand Down
2 changes: 1 addition & 1 deletion packages/components/transfer/src/TransferOperations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export default defineComponent({
return slots.operations(convertToSlotParams(transferOperationsContext))
}

if (transferProps.mode === 'transferBySelect') {
if (transferProps.mode === 'immediate') {
return
}

Expand Down
13 changes: 4 additions & 9 deletions packages/components/transfer/src/composables/usePagination.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ import type { PaginationProps } from '@idux/components/pagination'

import { type ComputedRef, computed, ref, watchEffect } from 'vue'

import { isArray } from 'lodash-es'

import { convertArray } from '@idux/cdk/utils'
import { useGlobalConfig } from '@idux/components/config'

export interface TransferPaginationContext {
Expand Down Expand Up @@ -48,13 +47,9 @@ function createPagination(

const idx = isSource ? 0 : 1

const pageIndex = isArray(pagination.pageIndex)
? pagination.pageIndex
: ([pagination.pageIndex] as [number | undefined])
const pageSize = isArray(pagination.pageSize)
? pagination.pageSize
: ([pagination.pageSize] as [number | undefined])
const total = isArray(pagination.total) ? pagination.total : ([pagination.total] as [number | undefined])
const pageIndex = convertArray(pagination.pageIndex)
const pageSize = convertArray(pagination.pageSize)
const total = convertArray(pagination.total)

return {
pageIndex: pageIndex?.[idx],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,15 @@ export function useTransferOperations<T extends TransferData = TransferData>(
return
}

append(keys ?? Array.from(sourceSelectedKeys.value))
append((keys ?? Array.from(sourceSelectedKeys.value)).filter(key => !disabledSourceKeys.value.has(key)))
}

const triggerRemove = (keys?: VKey[]) => {
if ((!keys && removeDisabled.value) || (keys && props.disabled)) {
return
}

remove(keys ?? Array.from(targetSelectedKeys.value))
remove((keys ?? Array.from(targetSelectedKeys.value)).filter(key => !disabledTargetKeys.value.has(key)))
}

const triggerAppendAll = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,27 +47,27 @@ export function useTransferSelectState(
const { dataKeyMap, sourceDataKeys, targetDataKeys, disabledKeys, disabledSourceKeys, disabledTargetKeys } =
transferDataContext

const sourceDataCount = computed(() =>
props.mode === 'immediate' ? dataKeyMap.value.size : sourceDataKeys.value.size,
)
const targetDataCount = computed(() => targetDataKeys.value.size)
const sourceCheckableDataCount = computed(() => {
const allCount = props.mode === 'transferBySelect' ? dataKeyMap.value.size : sourceDataKeys.value.size
const disabledCount = props.mode === 'transferBySelect' ? disabledKeys.value.size : disabledSourceKeys.value.size
const disabledCount = props.mode === 'immediate' ? disabledKeys.value.size : disabledSourceKeys.value.size

return allCount - disabledCount
return sourceDataCount.value - disabledCount
})
const targetCheckableDataCount = computed(() => targetDataKeys.value.size - disabledTargetKeys.value.size)

const sourceSelectAllStatus = computed(() => {
return {
checked:
sourceCheckableDataCount.value === sourceSelectedKeys.value.length && sourceSelectedKeys.value.length > 0,
indeterminate:
sourceCheckableDataCount.value > sourceSelectedKeys.value.length && sourceSelectedKeys.value.length > 0,
checked: sourceDataCount.value >= sourceSelectedKeys.value.length && sourceSelectedKeys.value.length > 0,
indeterminate: sourceDataCount.value > sourceSelectedKeys.value.length && sourceSelectedKeys.value.length > 0,
}
})
const targetSelectAllStatus = computed(() => {
return {
checked:
targetCheckableDataCount.value === targetSelectedKeys.value.length && targetSelectedKeys.value.length > 0,
indeterminate:
targetCheckableDataCount.value > targetSelectedKeys.value.length && targetSelectedKeys.value.length > 0,
checked: targetDataCount.value >= targetSelectedKeys.value.length && targetSelectedKeys.value.length > 0,
indeterminate: targetDataCount.value > targetSelectedKeys.value.length && targetSelectedKeys.value.length > 0,
}
})

Expand All @@ -82,12 +82,12 @@ export function useTransferSelectState(
return
}

if (props.mode === 'normal' && targetDataKeys.value.has(key)) {
if (props.mode === 'default' && targetDataKeys.value.has(key)) {
tempKeys.delete(key)
}
})

if (props.mode === 'transferBySelect') {
if (props.mode === 'immediate') {
targetDataKeys.value.forEach(key => {
if (dataKeyMap.value.has(key)) {
tempKeys.add(key)
Expand Down Expand Up @@ -127,47 +127,51 @@ export function useTransferSelectState(

const currentSelectedKeys = new Set<VKey>(keys)

if (props.mode === 'transferBySelect') {
if (props.mode === 'immediate') {
transferBySelectionChange(sourceSelectedKeySet.value, currentSelectedKeys, transferDataContext)
}

setSourceSelectedKeys((isArray(keys) ? keys : Array.from(keys)).filter(key => !disabledKeys.value.has(key)))
setSourceSelectedKeys(isArray(keys) ? keys : Array.from(keys))
}
const handleTargetSelectChange = (keys: VKey[] | Set<VKey>) => {
if (props.disabled) {
return
}

setTargetSelectedKeys(isArray(keys) ? keys : Array.from(keys).filter(key => !disabledKeys.value.has(key)))
setTargetSelectedKeys(isArray(keys) ? keys : Array.from(keys))
}
const handleSelectAll = (selected = true, isSource = true) => {
if (props.disabled) {
return
}

const dataKeys = isSource
? props.mode === 'normal'
? props.mode === 'default'
? sourceDataKeys.value
: new Set(dataKeyMap.value.keys())
: targetDataKeys.value
const _disabledKeys = isSource
? props.mode === 'normal'
? props.mode === 'default'
? disabledSourceKeys.value
: disabledKeys.value
: disabledTargetKeys.value
const selectedKeySet = isSource ? sourceSelectedKeySet : targetSelectedKeySet
const setSelectedKeys = isSource ? setSourceSelectedKeys : setTargetSelectedKeys

let tempKeys: Set<VKey>
if (!selected) {
tempKeys = new Set()
_disabledKeys.forEach(key => {
selectedKeySet.value.has(key) && tempKeys.add(key)
})
} else {
tempKeys = new Set(dataKeys)
_disabledKeys.forEach(key => {
tempKeys.delete(key)
!selectedKeySet.value.has(key) && tempKeys.delete(key)
})
}

if (props.mode === 'transferBySelect' && isSource) {
if (props.mode === 'immediate' && isSource) {
transferBySelectionChange(sourceSelectedKeySet.value, tempKeys, transferDataContext)
}

Expand Down

0 comments on commit e367009

Please sign in to comment.