20260720172840

This commit is contained in:
oneao committed 2026-07-20 17:28:47 +08:00
1 parent cda7f16d3a
commit 94207c77ff
3117 files changed
+201407 -1236

No files matched your search

@@ -0,0 +1,63 @@
# _semantic
## Source
```vue
<script setup lang="ts">
import { computed } from 'vue'
import { SemanticPreview } from '@/components/semantic'
import { useComponentLocale } from '@/composables/use-locale'
import { locales } from '../locales'
const { t } = useComponentLocale(locales)
const semantics = computed(() => [
{ name: 'root', desc: t('root') },
{ name: 'section', desc: t('section') },
{ name: 'header', desc: t('header') },
{ name: 'title', desc: t('title') },
{ name: 'body', desc: t('body') },
{ name: 'list', desc: t('list') },
{ name: 'item', desc: t('item') },
{ name: 'itemIcon', desc: t('itemIcon') },
{ name: 'itemContent', desc: t('itemContent') },
{ name: 'footer', desc: t('footer') },
{ name: 'actions', desc: t('actions') },
{ name: 'source', desc: t('source'), version: '1.3.0' },
{ name: 'target', desc: t('target'), version: '1.3.0' },
])
const mockData = Array.from({ length: 20 }).map((_, i) => ({
key: i,
title: `content ${i + 1}`,
}))
</script>
<template>
<SemanticPreview
component-name="Transfer"
:semantics="semantics"
>
<template #default="{ classes }">
<a-transfer
show-search
:titles="['Source', 'Target']"
:data-source="mockData"
:selected-keys="[]"
:target-keys="[3, 9]"
:render="(item: any) => item.title"
:styles="{
section: { height: '250px', width: '200px' },
}"
:classes="classes"
>
<template #footer>
<div :style="{ padding: '8px' }">
Custom Footer
</div>
</template>
</a-transfer>
</template>
</SemanticPreview>
</template>
```
@@ -0,0 +1,121 @@
# Custom Actions
## Description (en-US)
You can customize operations with the `actions` prop. This example demonstrates how to customize actions, including handling disabled and loading states.
When `actions` is an array of strings, it will use the default Button component and set the strings as button text.
When `actions` is an array of Vue nodes, it will use these elements directly as action buttons, allowing you to use custom button components, such as buttons with loading state in this example.
Note:
1. When using custom buttons, the Transfer component will automatically handle the button's disabled state and click events.
2. You can add a `disabled` property to your custom button to control its disabled state.
3. You can add an `onClick` event handler to your custom button, which will be merged with the Transfer component's internal handler.
## Source
```vue
<script setup lang="ts">
import type { TransferEmits } from 'antdv-next'
import { DoubleLeftOutlined, DoubleRightOutlined } from '@antdv-next/icons'
import { Button, message } from 'antdv-next'
import { computed, h, ref } from 'vue'
interface RecordType {
key: string
title: string
description: string
}
const mockData = Array.from({ length: 20 }).map<RecordType>((_, i) => ({
key: i.toString(),
title: `Content ${i + 1}`,
description: `Description ${i + 1}`,
}))
const initialTargetKeys = mockData.filter(item => Number(item.key) > 10).map(item => item.key)
const targetKeys = ref<string[]>(initialTargetKeys)
const selectedKeys = ref<string[]>([])
const loadingRight = ref(false)
const loadingLeft = ref(false)
const handleChange: TransferEmits['change'] = (newTargetKeys, direction, moveKeys) => {
targetKeys.value = newTargetKeys as string[]
if (direction === 'right') {
loadingRight.value = true
setTimeout(() => {
loadingRight.value = false
message.success(`Successfully added ${moveKeys.length} items to the right`)
}, 1000)
}
else {
loadingLeft.value = true
setTimeout(() => {
loadingLeft.value = false
message.success(`Successfully added ${moveKeys.length} items to the left`)
}, 1000)
}
}
const handleSelectChange: TransferEmits['selectChange'] = (sourceSelectedKeys, targetSelectedKeys) => {
selectedKeys.value = [...sourceSelectedKeys, ...targetSelectedKeys] as string[]
}
const rightButtonDisabled = computed(() =>
selectedKeys.value.length === 0 || selectedKeys.value.every(key => targetKeys.value.includes(key)),
)
const leftButtonDisabled = computed(() =>
selectedKeys.value.length === 0 || selectedKeys.value.every(key => !targetKeys.value.includes(key)),
)
function handleRightButtonClick(event: MouseEvent) {
console.log('Right button clicked', event)
}
function handleLeftButtonClick(event: MouseEvent) {
console.log('Left button clicked', event)
}
const actions = computed(() => [
h(
Button,
{
type: 'primary',
icon: h(DoubleRightOutlined),
loading: loadingRight.value,
disabled: rightButtonDisabled.value,
onClick: handleRightButtonClick,
},
{ default: () => 'Move To Right' },
),
h(
Button,
{
type: 'primary',
icon: h(DoubleLeftOutlined),
loading: loadingLeft.value,
disabled: leftButtonDisabled.value,
onClick: handleLeftButtonClick,
},
{ default: () => 'Move To Left' },
),
])
</script>
<template>
<a-transfer
v-model:target-keys="targetKeys"
v-model:selected-keys="selectedKeys"
:data-source="mockData"
:actions="actions"
:render="(item: RecordType) => item.title"
@change="handleChange"
@select-change="handleSelectChange"
/>
</template>
```
@@ -0,0 +1,91 @@
# Advanced
## Description (en-US)
Advanced Usage of Transfer.
You can customize the labels of the transfer buttons, the width and height of the columns, and what should be displayed in the footer.
## Source
```vue
<script setup lang="ts">
import type { TransferEmits, TransferProps } from 'antdv-next'
import { onMounted, ref } from 'vue'
interface RecordType {
key: string
title: string
description: string
chosen: boolean
}
const mockData = ref<RecordType[]>([])
const targetKeys = ref<TransferProps['targetKeys']>([])
function getMock() {
const tempTargetKeys: string[] = []
const tempMockData: RecordType[] = []
for (let i = 0; i < 20; i++) {
const data = {
key: i.toString(),
title: `content${i + 1}`,
description: `description of content${i + 1}`,
chosen: i % 2 === 0,
}
if (data.chosen) {
tempTargetKeys.push(data.key)
}
tempMockData.push(data)
}
mockData.value = tempMockData
targetKeys.value = tempTargetKeys
}
onMounted(() => {
getMock()
})
const handleChange: TransferEmits['change'] = (newTargetKeys) => {
targetKeys.value = newTargetKeys
}
const panelStyles: TransferProps['styles'] = {
section: {
width: 250,
height: 300,
},
}
</script>
<template>
<a-transfer
v-model:target-keys="targetKeys"
:data-source="mockData"
show-search
:styles="panelStyles"
:actions="['to right', 'to left']"
:render="(item: RecordType) => `${item.title}-${item.description}`"
@change="handleChange"
>
<template #footer="{ info }">
<a-button
v-if="info?.direction === 'left'"
size="small"
style="display: flex; margin: 8px; margin-inline-end: auto"
@click="getMock"
>
Left button reload
</a-button>
<a-button
v-else
size="small"
style="display: flex; margin: 8px; margin-inline-start: auto"
@click="getMock"
>
Right button reload
</a-button>
</template>
</a-transfer>
</template>
```
@@ -0,0 +1,62 @@
# Basic
## Description (en-US)
The most basic usage of `Transfer` involves providing the source data and target keys arrays, plus the rendering and some callback functions.
## Source
```vue
<script setup lang="ts">
import type { TransferEmits, TransferProps } from 'antdv-next'
import { ref } from 'vue'
interface RecordType {
key: string
title: string
description: string
}
const mockData = Array.from({ length: 20 }).map<RecordType>((_, i) => ({
key: i.toString(),
title: `content${i + 1}`,
description: `description of content${i + 1}`,
}))
const initialTargetKeys = mockData.filter(item => Number(item.key) > 10).map(item => item.key)
const targetKeys = ref<TransferProps['targetKeys']>(initialTargetKeys)
const selectedKeys = ref<TransferProps['targetKeys']>([])
const handleChange: TransferEmits['change'] = (nextTargetKeys, direction, moveKeys) => {
console.log('targetKeys:', nextTargetKeys)
console.log('direction:', direction)
console.log('moveKeys:', moveKeys)
targetKeys.value = nextTargetKeys
}
const handleSelectChange: TransferEmits['selectChange'] = (sourceSelectedKeys, targetSelectedKeys) => {
console.log('sourceSelectedKeys:', sourceSelectedKeys)
console.log('targetSelectedKeys:', targetSelectedKeys)
selectedKeys.value = [...sourceSelectedKeys, ...targetSelectedKeys]
}
const handleScroll: TransferEmits['scroll'] = (direction, e) => {
console.log('direction:', direction)
console.log('target:', (e.target as HTMLElement | null)?.className)
}
</script>
<template>
<a-transfer
v-model:target-keys="targetKeys"
v-model:selected-keys="selectedKeys"
:data-source="mockData"
:titles="['Source', 'Target']"
:render="(item: RecordType) => item.title"
@change="handleChange"
@select-change="handleSelectChange"
@scroll="handleScroll"
/>
</template>
```
@@ -0,0 +1,85 @@
# Custom datasource
## Description (en-US)
Customize each Transfer Item, allowing you to render a complex datasource.
## Source
```vue
<script setup lang="ts">
import type { TransferEmits, TransferProps } from 'antdv-next'
import { h, onMounted, ref } from 'vue'
interface RecordType {
key: string
title: string
description: string
chosen: boolean
}
const mockData = ref<RecordType[]>([])
const targetKeys = ref<TransferProps['targetKeys']>([])
function getMock() {
const tempTargetKeys: string[] = []
const tempMockData: RecordType[] = []
for (let i = 0; i < 20; i++) {
const data = {
key: i.toString(),
title: `content${i + 1}`,
description: `description of content${i + 1}`,
chosen: i % 2 === 0,
}
if (data.chosen) {
tempTargetKeys.push(data.key)
}
tempMockData.push(data)
}
mockData.value = tempMockData
targetKeys.value = tempTargetKeys
}
onMounted(() => {
getMock()
})
const handleChange: TransferEmits['change'] = (newTargetKeys, direction, moveKeys) => {
console.log(newTargetKeys, direction, moveKeys)
targetKeys.value = newTargetKeys
}
function renderItem(item: RecordType) {
const customLabel = h('span', { class: 'transfer-custom-item' }, `${item.title} - ${item.description}`)
return {
label: customLabel,
value: item.title,
}
}
const panelStyles: TransferProps['styles'] = {
section: {
width: '300px',
height: '300px',
},
}
</script>
<template>
<a-transfer
v-model:target-keys="targetKeys"
:data-source="mockData"
:styles="panelStyles"
:render="renderItem"
@change="handleChange"
/>
</template>
<style scoped>
.transfer-custom-item {
display: inline-flex;
align-items: center;
gap: 4px;
}
</style>
```
@@ -0,0 +1,49 @@
# Custom Select All Labels
## Description (en-US)
Custom the labels for select all checkboxes.
## Source
```vue
<script setup lang="ts">
import type { TransferEmits, TransferProps } from 'antdv-next'
import { ref } from 'vue'
interface RecordType {
key: string
title: string
description: string
}
const mockData = Array.from({ length: 10 }).map<RecordType>((_, i) => ({
key: i.toString(),
title: `content${i + 1}`,
description: `description of content${i + 1}`,
}))
const oriTargetKeys = mockData.filter(item => Number(item.key) % 3 > 1).map(item => item.key)
const selectAllLabels: TransferProps['selectAllLabels'] = [
'Select All',
({ selectedCount, totalCount }) => `${selectedCount}/${totalCount}`,
]
const targetKeys = ref(oriTargetKeys)
const handleChange: TransferEmits['change'] = (newTargetKeys) => {
targetKeys.value = newTargetKeys
}
</script>
<template>
<a-transfer
v-model:target-keys="targetKeys"
:data-source="mockData"
:render="(item: RecordType) => item.title"
:select-all-labels="selectAllLabels"
@change="handleChange"
/>
</template>
```
@@ -0,0 +1,68 @@
# Pagination
## Description (en-US)
Store a large amount of items with pagination.
## Source
```vue
<script setup lang="ts">
import type { TransferEmits } from 'antdv-next'
import { onMounted, ref } from 'vue'
interface RecordType {
key: string
title: string
description: string
chosen: boolean
}
const oneWay = ref(false)
const mockData = ref<RecordType[]>([])
const targetKeys = ref<string[]>([])
onMounted(() => {
const newTargetKeys: string[] = []
const newMockData: RecordType[] = []
for (let i = 0; i < 2000; i++) {
const data = {
key: i.toString(),
title: `content${i + 1}`,
description: `description of content${i + 1}`,
chosen: i % 2 === 0,
}
if (data.chosen) {
newTargetKeys.push(data.key)
}
newMockData.push(data)
}
targetKeys.value = newTargetKeys
mockData.value = newMockData
})
const handleChange: TransferEmits['change'] = (newTargetKeys, direction, moveKeys) => {
console.log(newTargetKeys, direction, moveKeys)
targetKeys.value = newTargetKeys
}
</script>
<template>
<div>
<a-transfer
v-model:target-keys="targetKeys"
:data-source="mockData"
:render="(item: RecordType) => item.title"
:one-way="oneWay"
pagination
@change="handleChange"
/>
<a-switch
v-model:checked="oneWay"
checked-children="one way"
un-checked-children="one way"
style="margin-top: 16px"
/>
</div>
</template>
```
@@ -0,0 +1,75 @@
# One Way
## Description (en-US)
Use `oneWay` to make Transfer the one way style.
## Source
```vue
<script setup lang="ts">
import type { TransferEmits } from 'antdv-next'
import { ref } from 'vue'
interface RecordType {
key: string
title: string
description: string
disabled: boolean
}
const mockData = Array.from({ length: 20 }).map<RecordType>((_, i) => ({
key: i.toString(),
title: `content${i + 1}`,
description: `description of content${i + 1}`,
disabled: i % 3 < 1,
}))
const oriTargetKeys = mockData.filter(item => Number(item.key) % 3 > 1).map(item => item.key)
const targetKeys = ref(oriTargetKeys)
const selectedKeys = ref<string[]>([])
const disabled = ref(false)
const handleChange: TransferEmits['change'] = (newTargetKeys, direction, moveKeys) => {
targetKeys.value = newTargetKeys
console.log('targetKeys: ', newTargetKeys)
console.log('direction: ', direction)
console.log('moveKeys: ', moveKeys)
}
const handleSelectChange: TransferEmits['selectChange'] = (sourceSelectedKeys, targetSelectedKeys) => {
selectedKeys.value = [...sourceSelectedKeys, ...targetSelectedKeys] as string[]
console.log('sourceSelectedKeys: ', sourceSelectedKeys)
console.log('targetSelectedKeys: ', targetSelectedKeys)
}
const handleScroll: TransferEmits['scroll'] = (direction, e) => {
console.log('direction:', direction)
console.log('target:', (e.target as HTMLElement | null)?.className)
}
</script>
<template>
<div>
<a-transfer
v-model:target-keys="targetKeys"
v-model:selected-keys="selectedKeys"
:data-source="mockData"
:titles="['Source', 'Target']"
:render="(item: RecordType) => item.title"
:disabled="disabled"
one-way
@change="handleChange"
@select-change="handleSelectChange"
@scroll="handleScroll"
/>
<a-switch
v-model:checked="disabled"
checked-children="disabled"
un-checked-children="disabled"
style="margin-top: 16px"
/>
</div>
</template>
```
@@ -0,0 +1,71 @@
# Search
## Description (en-US)
Transfer with a search box.
## Source
```vue
<script setup lang="ts">
import type { TransferEmits, TransferProps } from 'antdv-next'
import { onMounted, ref } from 'vue'
interface RecordType {
key: string
title: string
description: string
chosen: boolean
}
const mockData = ref<RecordType[]>([])
const targetKeys = ref<TransferProps['targetKeys']>([])
function getMock() {
const tempTargetKeys: string[] = []
const tempMockData: RecordType[] = []
for (let i = 0; i < 20; i++) {
const data = {
key: i.toString(),
title: `content${i + 1}`,
description: `description of content${i + 1}`,
chosen: i % 2 === 0,
}
if (data.chosen) {
tempTargetKeys.push(data.key)
}
tempMockData.push(data)
}
mockData.value = tempMockData
targetKeys.value = tempTargetKeys
}
onMounted(() => {
getMock()
})
function filterOption(inputValue: string, option: RecordType) {
return option.description.includes(inputValue)
}
const handleChange: TransferEmits['change'] = (newTargetKeys) => {
targetKeys.value = newTargetKeys
}
const handleSearch: TransferEmits['search'] = (direction, value) => {
console.log('search:', direction, value)
}
</script>
<template>
<a-transfer
v-model:target-keys="targetKeys"
:data-source="mockData"
show-search
:filter-option="filterOption"
:render="(item: RecordType) => item.title"
@change="handleChange"
@search="handleSearch"
/>
</template>
```
@@ -0,0 +1,20 @@
# Status
## Description (en-US)
Add status to Transfer with `status`, which could be `error` or `warning`.
## Source
```vue
<script setup lang="ts">
const emptyData: any[] = []
</script>
<template>
<a-flex vertical gap="middle">
<a-transfer :data-source="emptyData" status="error" />
<a-transfer :data-source="emptyData" status="warning" show-search />
</a-flex>
</template>
```
@@ -0,0 +1,80 @@
# Custom semantic dom styling
## Description (en-US)
You can customize the semantic dom style of Transfers by passing objects/functions through `classes` and `styles`.
## Source
```vue
<script setup lang="ts">
import type { TransferProps } from 'antdv-next'
import { ref } from 'vue'
const mockData = Array.from({ length: 20 }).map<any>((_, i) => ({
key: i.toString(),
title: `content${i + 1}`,
description: `description of content${i + 1}`,
}))
const initialTargetKeys = mockData.filter(item => Number(item.key) > 10).map(item => item.key)
const targetKeysError = ref(initialTargetKeys)
const targetKeysWarning = ref(initialTargetKeys)
const classes: TransferProps['classes'] = {
section: 'transfer-demo-section',
header: 'transfer-demo-header',
actions: 'transfer-demo-actions',
}
const stylesObject: TransferProps['styles'] = {
header: { fontWeight: 'bold' },
}
const stylesFn: TransferProps['styles'] = (info) => {
if (info.props.status === 'warning') {
return {
section: { backgroundColor: 'rgba(246,255,237, 0.6)', borderColor: '#b7eb8f' },
header: { color: '#8DBCC7', fontWeight: 'normal' },
} as TransferProps['styles']
}
return {}
}
</script>
<template>
<a-flex vertical gap="large" style="width: 100%">
<a-transfer
v-model:target-keys="targetKeysError"
:data-source="mockData"
:render="(item) => item.title"
status="error"
:classes="classes"
:styles="stylesObject"
/>
<a-transfer
v-model:target-keys="targetKeysWarning"
:data-source="mockData"
:render="(item) => item.title"
status="warning"
:classes="classes"
:styles="stylesFn"
/>
</a-flex>
</template>
<style scoped>
.transfer-demo-section {
background-color: rgba(250, 250, 250, 0.5);
}
.transfer-demo-header {
color: var(--ant-color-primary);
}
.transfer-demo-actions button {
background-color: rgba(255, 242, 232, 0.6);
}
</style>
```
@@ -0,0 +1,151 @@
# Table Transfer
## Description (en-US)
Use the Table component as the customize render list.
## Source
```vue
<script setup lang="ts">
import type { TableProps, TransferEmits, TransferProps } from 'antdv-next'
import { Table } from 'antdv-next'
import { ref } from 'vue'
interface DataType {
key: string
title: string
description: string
tag: string
disabled?: boolean
}
const mockTags = ['cat', 'dog', 'bird'] as const
const mockData: DataType[] = Array.from({ length: 20 }, (_, i) => ({
key: String(i),
title: `content${i + 1}`,
description: `description of content${i + 1}`,
tag: mockTags[i % 3]!,
}))
const targetKeys = ref<string[]>([])
const interactionEnabled = ref(false)
const columns: TableProps['columns'] = [
{ title: 'Name', dataIndex: 'title', key: 'title' },
{ title: 'Tag', dataIndex: 'tag', key: 'tag' },
{ title: 'Description', dataIndex: 'description', key: 'description' },
]
const filterOption: NonNullable<TransferProps['filterOption']> = (input: string, item: DataType) => {
const q = input.trim().toLowerCase()
if (!q)
return true
return item.title!.toLowerCase().includes(q)
}
const handleChange: TransferEmits['change'] = (keys) => {
targetKeys.value = keys as string[]
}
const panelStyles: TransferProps['styles'] = {
section: {
width: 420,
minHeight: 520,
},
}
const tablePagination: TableProps['pagination'] = {
pageSize: 10,
showSizeChanger: false,
hideOnSinglePage: false,
}
interface ListSlotProps {
direction: 'left' | 'right'
disabled: boolean
filteredItems: DataType[]
selectedKeys: string[]
onItemSelect: (key: string, selected: boolean) => void
onItemSelectAll: (keys: string[], checkAll: boolean | 'replace') => void
}
function tableRowSelection(slot: ListSlotProps): TableProps['rowSelection'] {
return {
getCheckboxProps: record => ({
disabled: slot.disabled || Boolean((record as DataType).disabled),
}),
onChange: (selectedRowKeys) => {
slot.onItemSelectAll(selectedRowKeys as string[], 'replace')
},
selectedRowKeys: slot.selectedKeys,
selections: [Table.SELECTION_ALL, Table.SELECTION_INVERT, Table.SELECTION_NONE],
}
}
function tableOnRow(slot: ListSlotProps): TableProps['onRow'] {
return (record) => {
const row = record as DataType
return {
onClick: (e: MouseEvent) => {
if (slot.disabled || row.disabled)
return
const el = e.target as HTMLElement | null
if (!el)
return
if (el.closest('.ant-table-selection-column'))
return
const key = row.key
const selected = slot.selectedKeys.includes(key)
slot.onItemSelect(key, !selected)
},
style: { cursor: slot.disabled || row.disabled ? undefined : 'pointer' },
}
}
}
</script>
<template>
<div>
<a-transfer
v-model:target-keys="targetKeys"
:data-source="mockData"
:disabled="interactionEnabled"
:show-select-all="false"
show-search
:filter-option="filterOption"
:styles="panelStyles"
:render="(item: DataType) => item.title"
@change="handleChange"
>
<template #default="slotProps">
<a-table
size="small"
:row-selection="tableRowSelection(slotProps as ListSlotProps)"
:on-row="tableOnRow(slotProps as ListSlotProps)"
:columns="columns"
:data-source="slotProps.filteredItems"
:pagination="tablePagination"
>
<template #bodyCell="{ column, text }">
<template v-if="column.key === 'tag'">
<a-tag color="cyan">
{{ String(text).toUpperCase() }}
</a-tag>
</template>
</template>
</a-table>
</template>
</a-transfer>
<a-space align="center" style="margin-top: 12px">
<a-switch
v-model:checked="interactionEnabled"
checked-children="disabled"
un-checked-children="disabled"
/>
</a-space>
</div>
</template>
```
@@ -0,0 +1,99 @@
# Tree Transfer
## Description (en-US)
Customize the render list with a Tree component.
## Source
```vue
<script setup lang="ts">
import type { TransferEmits } from 'antdv-next'
import { ref } from 'vue'
interface TreeNode {
key: string
title: string
children?: TreeNode[]
}
const treeData: TreeNode[] = [
{ key: '0-0', title: '0-0' },
{
key: '0-1',
title: '0-1',
children: [
{ key: '0-1-0', title: '0-1-0' },
{ key: '0-1-1', title: '0-1-1' },
],
},
{ key: '0-2', title: '0-2' },
{ key: '0-3', title: '0-3' },
{ key: '0-4', title: '0-4' },
]
const targetKeys = ref<string[]>([])
function flattenData(list: TreeNode[] = [], result: TreeNode[] = []) {
list.forEach((item) => {
result.push(item)
if (item.children) {
flattenData(item.children, result)
}
})
return result
}
const transferDataSource = flattenData(treeData, []).map(item => ({
key: item.key,
title: item.title,
}))
const isChecked = (selectedKeys: string[], eventKey: string) => selectedKeys.includes(eventKey)
function generateTree(treeNodes: TreeNode[] = [], checkedKeys: string[] = []): TreeNode[] {
return treeNodes.map(({ children, ...props }) => ({
...props,
disabled: checkedKeys.includes(props.key),
children: children ? generateTree(children, checkedKeys) : undefined,
}))
}
const handleChange: TransferEmits['change'] = (keys) => {
targetKeys.value = keys as string[]
}
</script>
<template>
<a-transfer
v-model:target-keys="targetKeys"
class="tree-transfer"
:data-source="transferDataSource"
:render="(item) => item.title"
:show-select-all="false"
@change="handleChange"
>
<template #default="{ direction, selectedKeys, onItemSelect }">
<div v-if="direction === 'left'" style="padding: 8px">
<a-tree
block-node
checkable
check-strictly
default-expand-all
:checked-keys="[...selectedKeys, ...targetKeys]"
:tree-data="generateTree(treeData, targetKeys)"
@check="(_, info) => onItemSelect(info.node.key, !isChecked([...selectedKeys, ...targetKeys], info.node.key))"
@select="(_, info) => onItemSelect(info.node.key, !isChecked([...selectedKeys, ...targetKeys], info.node.key))"
/>
</div>
</template>
</a-transfer>
</template>
<style scoped>
:deep(.tree-transfer .ant-transfer-list:first-child) {
flex: none;
width: 50%;
}
</style>
```
@@ -0,0 +1,114 @@
---
title: Transfer
description: Double column transfer choice box.
---
## When To Use
- It is a select control essentially which can be use for selecting multiple items.
- Transfer can display more information for items and take up more space.
Transfer the elements between two columns in an intuitive and efficient way.
One or more elements can be selected from either column, one click on the proper `direction` button, and the transfer is done. The left column is considered the `source` and the right column is considered the `target`. As you can see in the API description, these names are reflected in.
> Notice: Transfer is a controlled component, uncontrolled mode is not supported.
## Demos
| Demo | Path |
| --- | --- |
| Basic | demo/basic.md |
| One Way | demo/oneWay.md |
| Search | demo/search.md |
| Advanced | demo/advanced.md |
| Custom datasource | demo/custom-item.md |
| Custom Actions | demo/actions.md |
| Pagination | demo/large-data.md |
| Table Transfer | demo/table-transfer.md |
| Tree Transfer | demo/tree-transfer.md |
| Status | demo/status.md |
| Custom semantic dom styling | demo/style-class.md |
| Custom Select All Labels | demo/custom-select-all-labels.md |
## API
Common props ref:[Common props](../../docs/vue/common-props.md)
### Props
| Property | Description | Type | Default | Version | [Global Config](../config-provider/docs.md#component-config) |
| --- | --- | --- | --- | --- | --- |
| actions | A set of operations that are sorted from top to bottom. When an array of strings is provided, default buttons will be used; when an array of VueNode is provided, custom elements will be used | VueNode[] | [`>`, `<`] | 6.0.0 | × |
| classes | Customize class for each semantic structure inside the component. Supports object or function | TransferClassNamesType | - | - | ✓ |
| dataSource | Used for setting the source data. The elements that are part of this array will be present the left column. Except the elements whose keys are included in `targetKeys` prop | TransferItem[] | [] | - | × |
| disabled | Whether disabled transfer | boolean | false | - | × |
| filterOption | A function to determine whether an item should show in search result list, only works when searching | (inputValue: string, option: TransferItem, direction: `left` \| `right`) =&gt; boolean | - | - | × |
| footer | A function used for rendering the footer | (props: TransferListProps, direction: `left` \| `right`) =&gt; VueNode | - | - | × |
| locale | The i18n text including filter, empty text, item unit, etc | TransferLocale | - | - | × |
| oneWay | Display as single direction style | boolean | false | - | × |
| pagination | Use pagination. Not work in render props | boolean \| &#123; pageSize: number; simple: boolean; showSizeChanger?: boolean; showLessItems?: boolean &#125; | false | - | × |
| render | The function to generate the item shown on a column. Based on an record (element of the dataSource array), this function should return a VueNode which is generated from that record. Also, it can return a plain object with `value` and `label`, `label` is a VueNode and `value` is for title | (record: TransferItem) =&gt; VueNode | - | - | × |
| rowKey | Specify the key that will be used for uniquely identify each element | (record: TransferItem) =&gt; string | - | - | × |
| selectAllLabels | A set of customized labels for select all checkboxes on the header | (VueNode \| (info: &#123; selectedCount: number; totalCount: number &#125;) =&gt; VueNode)[] | - | - | × |
| selectedKeys | A set of keys of selected items, support `v-model:selected-keys` | string[] | [] | - | × |
| selectionsIcon | Custom dropdown icon | VueNode | - | - | ✓ |
| showSearch | If included, a search box is shown on each column | boolean \| &#123; placeholder?: string; defaultValue?: string &#125; | false | - | × |
| showSelectAll | Show select all checkbox on the header | boolean | true | - | × |
| status | Set validation status | `error` \| `warning` | - | - | × |
| styles | Customize inline style for each semantic structure inside the component. Supports object or function | TransferStylesType | - | - | ✓ |
| targetKeys | A set of keys of elements that are listed on the right column, support `v-model:target-keys` | string[] | [] | - | × |
| titles | A set of titles that are sorted from left to right | VueNode[] | - | - | × |
### Events
| Event | Description | Type | Version |
| --- | --- | --- | --- |
| change | A callback function that is executed when the transfer between columns is complete | (targetKeys: string[], direction: `left` \| `right`, moveKeys: string[]) =&gt; void | - |
| scroll | A callback function which is executed when scroll options list | (direction: `left` \| `right`, event: Event) =&gt; void | - |
| search | A callback function which is executed when search field are changed | (direction: `left` \| `right`, value: string) =&gt; void | - |
| selectChange | A callback function which is executed when selected items are changed | (sourceSelectedKeys: string[], targetSelectedKeys: string[]) =&gt; void | - |
### Slots
| Slot | Description | Type | Version |
| --- | --- | --- | --- |
| actions | Custom operation buttons | (direction: `left` \| `right`) =&gt; VueNode | - |
| footer | Custom footer | (props: TransferListProps, direction: `left` \| `right`) =&gt; VueNode | - |
| render | Custom item render | (item: TransferItem) =&gt; VueNode | - |
| notFoundContent | Custom empty content | (direction: `left` \| `right`) =&gt; VueNode | - |
| selectAllLabels | Custom select all label | (info: &#123; selectedCount: number; totalCount: number; direction: `left` \| `right` &#125;) =&gt; VueNode | - |
## Types
### Render Props
Transfer accept `children` to customize render list, using follow props:
| Property | Description | Type | Version |
| --- | --- | --- | --- |
| direction | List render direction | `left` \| `right` | - |
| disabled | Disable list or not | boolean | - |
| filteredItems | Filtered items | TransferItem[] | - |
| selectedKeys | Selected items | string[] | - |
| onItemSelect | Select item | (key: string, selected: boolean) =&gt; void | - |
| onItemSelectAll | Select a group of items | (keys: string[], selected: boolean) =&gt; void | - |
## Warning
According the standard of Vue, the key should always be supplied directly to the elements in the array. In Transfer, the keys should be set on the elements included in `dataSource` array. By default, `key` property is used as an unique identifier.
If there's no `key` in your data, you should use `rowKey` to specify the key that will be used for uniquely identify each element.
```vue
<!-- eg. your primary key is `uid` -->
<a-transfer :row-key="(record) => record.uid" />
```
## Semantic DOM
| _semantic | demo/_semantic.md |
## FAQ
### How to support fetch and present data from a remote server in Transfer column.
In order to keep the page number synchronized, you can disable columns you checked without removing the option.
@@ -0,0 +1,31 @@
# transfer Token
Use these variables through `theme.components.Transfer`. This document lists definitions only and does not include values.
```vue
<a-config-provider
:theme="{
token: {
// Global token configuration
},
components: {
Transfer: {
// Token name: value
},
},
}"
>
...
</a-config-provider>
```
## Token List
| Token | Type | Description |
| --- | --- | --- |
| `listWidth` | `string \| number` | Width of list |
| `listWidthLG` | `string \| number` | Width of large list |
| `listHeight` | `string \| number` | Height of list |
| `itemHeight` | `string \| number` | Height of list item |
| `itemPaddingBlock` | `string \| number` | Vertical padding of list item |
| `headerHeight` | `string \| number` | Height of header |