Skip to content

Transfer ​

Manba va nishon to‘plamlari o‘rtasida elementlarni ko‘chirish uchun ikki panelli ro‘yxat.

Source10
Item 1
Item 3
Item 4
Item 6
Item 7
Item 8
Item 9
Item 10
Item 11
Item 12
Target2
Item 2
Item 5
vue
<script setup lang="ts">
import { ref } from 'vue'
import { VxTransfer } from 'veloxui'

const data = Array.from({ length: 12 }, (_, i) => ({
  key: i + 1,
  label: `Item ${i + 1}`,
  disabled: i === 3 || i === 7,
}))

const value = ref([2, 5])
</script>

<template>
  <VxTransfer v-model="value" :data="data" />
</template>

Filterable ​

Source10
Item 2
Item 3
Item 5
Item 6
Item 7
Item 8
Item 9
Item 10
Item 11
Item 12
Target2
Item 1
Item 4
vue
<script setup lang="ts">
import { ref } from 'vue'

const data = Array.from({ length: 12 }, (_, i) => ({
  key: i + 1,
  label: `Item ${i + 1}`,
}))
const value = ref([1, 4])
</script>

<template>
  <VxTransfer v-model="value" :data="data" filterable />
</template>

Custom Titles ​

Available Cities6
Los Angeles
Houston
Phoenix
Philadelphia
San Antonio
San Diego
Selected Cities2
New York
Chicago
vue
<script setup lang="ts">
import { ref } from 'vue'

const cities = [
  { key: 'nyc', label: 'New York' },
  { key: 'la', label: 'Los Angeles' },
  { key: 'chi', label: 'Chicago' },
  { key: 'hou', label: 'Houston' },
  { key: 'phx', label: 'Phoenix' },
]
const value = ref(['nyc', 'chi'])
</script>

<template>
  <VxTransfer
    v-model="value"
    :data="cities"
    :titles="['Available Cities', 'Selected Cities']"
    filterable
  />
</template>

Field Mapping ​

Ma’lumotlaringizni qayta ishlamasdan maxsus xususiyatlarni moslashtirish uchun key-field va label-field’dan foydalaning.

Users4
Alice
Charlie
Diana
Eve
Team1
Bob
vue
<script setup lang="ts">
import { ref } from 'vue'

const users = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
  { id: 3, name: 'Charlie' },
  { id: 4, name: 'Diana' },
  { id: 5, name: 'Eve' },
]
const value = ref([2])
</script>

<template>
  <VxTransfer
    v-model="value"
    :data="users"
    key-field="id"
    label-field="name"
    :titles="['Users', 'Team']"
  />
</template>

Panel Width ​

Source4
Item 2
Item 4
Item 5
Item 6
Target2
Item 1
Item 3
vue
<script setup lang="ts">
import { ref } from 'vue'

const data = Array.from({ length: 6 }, (_, i) => ({
  key: i + 1,
  label: `Item ${i + 1}`,
}))
const value = ref([1, 3])
</script>

<template>
  <!-- Number = pixels -->
  <VxTransfer v-model="value" :data="data" :panel-width="300" />

  <!-- String = any CSS value -->
  <VxTransfer v-model="value" :data="data" panel-width="50%" />
</template>

Sort ​

Ikkala paneldagi elementlar taqdim etilgan comparator funksiyasi yordamida tartiblanadi.

Sorted A-Z10
Item 10
Item 11
Item 12
Item 2
Item 4
Item 5
Item 6
Item 7
Item 8
Item 9
Sorted A-Z2
Item 1
Item 3
vue
<script setup lang="ts">
import { ref } from 'vue'

const data = Array.from({ length: 12 }, (_, i) => ({
  key: i + 1,
  label: `Item ${i + 1}`,
}))
const value = ref([3, 1])
</script>

<template>
  <VxTransfer
    v-model="value"
    :data="data"
    :sort-method="(a, b) => a.label.localeCompare(b.label)"
    :titles="['Sorted A-Z', 'Sorted A-Z']"
  />
</template>

Move All ​

Barcha ruxsat etilgan elementlarni bir martaga ko‘chirish uchun "hammasini ko‘chirish" tugmalarini (qo‘sh strelkalar) ko‘rsating.

Source12
Item 1
Item 2
Item 3
Item 4
Item 5
Item 6
Item 7
Item 8
Item 9
Item 10
Item 11
Item 12
Target0
No data
vue
<script setup lang="ts">
import { ref } from 'vue'

const data = Array.from({ length: 12 }, (_, i) => ({
  key: i + 1,
  label: `Item ${i + 1}`,
  disabled: i === 3 || i === 7,
}))
const value = ref([])
</script>

<template>
  <VxTransfer v-model="value" :data="data" show-move-all />
</template>

Button Texts ​

Source12
Item 1
Item 2
Item 3
Item 4
Item 5
Item 6
Item 7
Item 8
Item 9
Item 10
Item 11
Item 12
Target0
No data
vue
<script setup lang="ts">
import { ref } from 'vue'

const data = Array.from({ length: 12 }, (_, i) => ({
  key: i + 1,
  label: `Item ${i + 1}`,
}))
const value = ref([])
</script>

<template>
  <VxTransfer v-model="value" :data="data" :button-texts="['Add', 'Remove']" />
</template>

Custom Button Slots ​

Har bir slotga move, disabled va checkedCount uzatiladi.

Source7
Item 1
Item 2
Item 3
Item 5
Item 6
Item 7
Item 8
Target1
Item 4
vue
<script setup lang="ts">
import { ref } from 'vue'

const data = Array.from({ length: 8 }, (_, i) => ({
  key: i + 1,
  label: `Item ${i + 1}`,
}))
const value = ref([4])
</script>

<template>
  <VxTransfer v-model="value" :data="data">
    <template #to-target-button="{ move, disabled, checkedCount }">
      <button :disabled="disabled" @click="move">
        Add {{ checkedCount || '' }} →
      </button>
    </template>
    <template #to-source-button="{ move, disabled, checkedCount }">
      <button :disabled="disabled" @click="move">
        ← Remove {{ checkedCount || '' }}
      </button>
    </template>
  </VxTransfer>
</template>

Custom Item Slot ​

Source11
Item 1#1
Item 2#2
Item 4#4
Item 5#5
Item 6#6
Item 7#7
Item 8#8
Item 9#9
Item 10#10
Item 11#11
Item 12#12
Target1
Item 3#3
vue
<script setup lang="ts">
import { ref } from 'vue'

const data = Array.from({ length: 12 }, (_, i) => ({
  key: i + 1,
  label: `Item ${i + 1}`,
}))
const value = ref([3])
</script>

<template>
  <VxTransfer v-model="value" :data="data">
    <template #item="{ item }">
      <span>{{ item.label }}</span>
      <span style="margin-left: auto; font-size: 11px; opacity: 0.5">
        #{{ item.key }}
      </span>
    </template>
  </VxTransfer>
</template>

Panel headerlari va footerlarini moslashtiring. Slotlarga { checked, total, query } uzatiladi.

Pick items (6)
Item 1
Item 2
Item 3
Item 4
Item 5
Item 6
Target0
No data
vue
<script setup lang="ts">
import { ref } from 'vue'

const data = Array.from({ length: 6 }, (_, i) => ({
  key: i + 1,
  label: `Item ${i + 1}`,
}))
const value = ref([])
</script>

<template>
  <VxTransfer v-model="value" :data="data">
    <template #source-header="{ total }">
      <div class="vx-transfer__header" style="background: var(--muted);">
        <span style="font-size: 13px; font-weight: 600;">
          Pick items ({{ total }})
        </span>
      </div>
    </template>
    <template #target-footer="{ total }">
      <div class="vx-transfer__footer">
        <span class="vx-transfer__footer-info">
          {{ total }} selected
        </span>
      </div>
    </template>
  </VxTransfer>
</template>

Drag and Drop ​

Elementlarni panellar orasida sudrab o‘tkazing. draggable bilan yoqing. Element ustiga sudralganda nishon paneli yoritiladi.

Source10
Item 1
Item 3
Item 4
Item 6
Item 7
Item 8
Item 9
Item 10
Item 11
Item 12
Target2
Item 2
Item 5
vue
<script setup lang="ts">
import { ref } from 'vue'

const data = Array.from({ length: 12 }, (_, i) => ({
  key: i + 1,
  label: `Item ${i + 1}`,
  disabled: i === 3 || i === 7,
}))
const value = ref([2, 5])
</script>

<template>
  <VxTransfer v-model="value" :data="data" draggable />
</template>

Keyboard Navigation ​

Panel ichini bosib fokuslang, keyin elementlar orasida harakat qilish uchun Yuqori/Pastki strelka, tanlovni o‘zgartirish uchun Space yoki Enter tugmalaridan foydalaning. Fokuslangan element kontur halqasi bilan belgilanadi.

Source12
Item 1
Item 2
Item 3
Item 4
Item 5
Item 6
Item 7
Item 8
Item 9
Item 10
Item 11
Item 12
Target0
No data
vue
<script setup lang="ts">
import { ref } from 'vue'

const data = Array.from({ length: 12 }, (_, i) => ({
  key: i + 1,
  label: `Item ${i + 1}`,
}))
const value = ref([])
</script>

<template>
  <!-- Click inside a panel, then use Arrow keys + Space/Enter -->
  <VxTransfer v-model="value" :data="data" />
</template>

Scroll Pagination ​

Foydalanuvchi scroll qilganda elementlarni bosqichma-bosqich yuklash uchun page-size’ni belgilang. Faqat ko‘rinayotgan sahifa renderlanadi — pastga yaqinlashganda qo‘shimcha elementlar avtomatik paydo bo‘ladi.

All Records (100)95
Record 1
Record 2
Record 3
Record 4
Record 6
Record 7
Record 8
Record 9
Record 11
Record 12
Record 13
Record 14
Record 15
Record 16
Record 17
Selected5
Record 5
Record 10
Record 20
Record 30
Record 40
vue
<script setup lang="ts">
import { ref } from 'vue'

const bigData = Array.from({ length: 100 }, (_, i) => ({
  key: i + 1,
  label: `Record ${i + 1}`,
  disabled: i === 9,
}))
const value = ref([5, 10, 20, 30, 40])
</script>

<template>
  <VxTransfer
    v-model="value"
    :data="bigData"
    :page-size="15"
    filterable
    :titles="['All Records (100)', 'Selected']"
  />
</template>

Virtual Scroll ​

Katta ma’lumotlar to‘plamlari uchun (1000+ element) virtual-scroll’ni item-height bilan yoqing — bu 60fps’da silliq scrollni ta’minlaydi. DOM’da faqat ko‘rinadigan qatorlar renderlanadi.

5,000 Items4997
Virtual item 1
Virtual item 2
Virtual item 3
Virtual item 4
Virtual item 5
Virtual item 6
Virtual item 7
Virtual item 8
Virtual item 9
Virtual item 10
Virtual item 11
Virtual item 12
Virtual item 13
Virtual item 14
Virtual item 15
Virtual item 16
Virtual item 17
Virtual item 18
Virtual item 19
Selected3
Virtual item 100
Virtual item 200
Virtual item 500
vue
<script setup lang="ts">
import { ref } from 'vue'

const hugeData = Array.from({ length: 5000 }, (_, i) => ({
  key: i + 1,
  label: `Virtual item ${i + 1}`,
}))
const value = ref([100, 200, 500])
</script>

<template>
  <VxTransfer
    v-model="value"
    :data="hugeData"
    virtual-scroll
    :item-height="36"
    filterable
    :titles="['5,000 Items', 'Selected']"
  />
</template>

Remote Data (fetchOptions) ​

Cheksiz scroll bilan API’dan manba ma’lumotlarini yuklang. TransferItem[] qaytaruvchi asinxron fetchOptions(query, page) funksiyasini uzating. Keyingi sahifani yuklash uchun pastga scroll qiling. Qidiruv inputi debounce qilingan va yangi fetch’ni ishga tushiradi.

Comments (remote)0
No data
Selected0
No data
vue
<script setup lang="ts">
import { ref } from 'vue'

async function fetchComments(query: string, page: number) {
  const res = await fetch(
    `https://jsonplaceholder.typicode.com/comments?_limit=10&_page=${page}`
  )
  const data = await res.json()
  return data.map((c: any) => ({ key: c.id, label: c.name }))
}

const value = ref([])
</script>

<template>
  <VxTransfer
    v-model="value"
    :fetch-options="fetchComments"
    filterable
    :titles="['Comments (remote)', 'Selected']"
  />
</template>

Loading ​

Tashqi loading holati — manba panelining pastki qismida spinner ko‘rsatadi.

Source3
Item 2
Item 3
Item 4
Target1
Item 1
vue
<script setup lang="ts">
import { ref } from 'vue'

const data = Array.from({ length: 4 }, (_, i) => ({
  key: i + 1,
  label: `Item ${i + 1}`,
}))
const value = ref([1])
const loading = ref(true)
</script>

<template>
  <VxTransfer v-model="value" :data="data" :loading="loading" />
</template>

Target Order ​

Odatda elementlar asl ma’lumotlar tartibini saqlaydi. Elementlarni tanlangan tartibda qo‘shish uchun target-order="push"’dan foydalaning.

Source11
Item 1
Item 3
Item 4
Item 5
Item 6
Item 7
Item 8
Item 9
Item 10
Item 11
Item 12
Target1
Item 2
vue
<script setup lang="ts">
import { ref } from 'vue'

const data = Array.from({ length: 12 }, (_, i) => ({
  key: i + 1,
  label: `Item ${i + 1}`,
}))
const value = ref([2])
</script>

<template>
  <VxTransfer v-model="value" :data="data" target-order="push" />
</template>

Button Visibility ​

Ko‘chirish tugmalaridan birini yoki ikkalasini yashiring.

Source4
Item 2
Item 4
Item 5
Item 6
Target2
Item 1
Item 3
vue
<script setup lang="ts">
import { ref } from 'vue'

const data = Array.from({ length: 6 }, (_, i) => ({
  key: i + 1,
  label: `Item ${i + 1}`,
}))
const value = ref([1, 3])
</script>

<template>
  <!-- Only allow adding to target (no remove button) -->
  <VxTransfer v-model="value" :data="data" :show-to-source-button="false" />

  <!-- Only allow removing from target (no add button) -->
  <VxTransfer v-model="value" :data="data" :show-to-target-button="false" />
</template>

Custom Empty State ​

Panelda elementlar bo‘lmaganda ko‘rsatiladigan kontentni moslashtirish uchun source-empty va target-empty slotlaridan foydalaning.

Source0
📭
Nothing here yet
Add items to get started
Target0
🎯
No selections
Move items from the left
vue
<template>
  <VxTransfer :data="[]">
    <template #source-empty>
      <div style="text-align: center; padding: 20px;">
        <div style="font-size: 24px;">📭</div>
        <div>Nothing here yet</div>
        <div style="opacity: 0.7;">Add items to get started</div>
      </div>
    </template>
    <template #target-empty>
      <div style="text-align: center; padding: 20px;">
        <div style="font-size: 24px;">🎯</div>
        <div>No selections</div>
        <div style="opacity: 0.7;">Move items from the left</div>
      </div>
    </template>
  </VxTransfer>
</template>

Disabled ​

Source6
Item 1
Item 2
Item 3
Item 4
Item 5
Item 6
Target0
No data
vue
<script setup lang="ts">
import { ref } from 'vue'

const data = Array.from({ length: 6 }, (_, i) => ({
  key: i + 1,
  label: `Item ${i + 1}`,
}))
const value = ref([])
</script>

<template>
  <VxTransfer v-model="value" :data="data" disabled />
</template>

Props ​

PropTypeDefaultDescription
modelValue(string | number)[][]Nishon ro‘yxatidagi kalitlar (v-model)
dataTransferItem[][]To‘liq ma’lumot manbasi
titles[string, string]['Source', 'Target']Panel sarlavhalari
filterablebooleanfalseQidiruv inputini ko‘rsatish
filterPlaceholderstring'Search...'Qidiruv placeholder
filterMethod(query, item) => boolean—Maxsus filtr funksiyasi
disabledbooleanfalseBarcha interaksiyani o‘chirib qo‘yish
loadingbooleanfalseTashqi loading holati
targetOrder'original' | 'push''original'Nishon elementlarining tartibi
buttonTexts[string?, string?][]Ko‘chirish tugmalari yozuvlari
showToTargetButtonbooleantrueNishonga ko‘chirish tugmasini ko‘rsatish
showToSourceButtonbooleantrueManbaga qaytarish tugmasini ko‘rsatish
showMoveAllbooleanfalseHammasini ko‘chirish tugmalarini ko‘rsatish
pageSizenumber0Bir sahifadagi elementlar soni (0 = hammasini ko‘rsatish)
scrollDistancenumber30Yuklashni boshlash uchun pastdan masofa (px)
fetchOptions(query, page) => Promise<TransferItem[]>—Asinxron masofaviy manba
debouncenumber300Masofaviy qidiruv uchun debounce (ms)
keyFieldstring'key'Element kaliti uchun xususiyat nomi
labelFieldstring'label'Element yozuvi uchun xususiyat nomi
panelWidthnumber | string240Panel kengligi (px yoki CSS qiymati)
sortMethod(a, b) => number—Elementlar uchun tartiblash comparatori
draggablebooleanfalsePanellar orasida drag & drop’ni yoqish
virtualScrollbooleanfalseKatta ro‘yxatlar uchun virtual scroll’ni yoqish
itemHeightnumber36Qator balandligi (px) — virtual scroll uchun

Events ​

EventTypeDescription
update:modelValue(value)Nishon kalitlari o‘zgardi
change(value, direction, movedKeys)Elementlar ko‘chirildi
load-more(page)Keyingi masofaviy sahifa so‘ralgan

Slots ​

SlotPropsDescription
item{ item }Elementni maxsus renderlash
source-header{ checked, total, query }Manba paneli uchun maxsus header
target-header{ checked, total, query }Nishon paneli uchun maxsus header
source-footer{ checked, total }Manba paneli uchun maxsus footer
target-footer{ checked, total }Nishon paneli uchun maxsus footer
source-empty—Manbaning bo‘sh holati
target-empty—Nishonning bo‘sh holati
to-target-button{ move, disabled, checkedCount }Nishonga ko‘chirish tugmasini moslashtirish
to-source-button{ move, disabled, checkedCount }Manbaga qaytarish tugmasini moslashtirish

Exposed Methods ​

MethodDescription
moveRight()Belgilangan elementlarni nishonga ko‘chirish
moveLeft()Belgilangan elementlarni manbaga qaytarish
moveAllRight()Barcha ruxsat etilgan elementlarni nishonga ko‘chirish
moveAllLeft()Barcha ruxsat etilgan elementlarni manbaga qaytarish

TransferItem Interface ​

ts
interface TransferItem {
  key: string | number
  label: string
  disabled?: boolean
  [k: string]: any
}