اتوکامپلیت کاربران
کامپوننت اختصاصی جستجوی کاربران با Infinite Scroll
معرفی
کامپوننت UserAutocomplete یک input اختصاصی برای جستجوی کاربران است که شامل:
- نمایش آواتار، نام، یوزرنیم و تعداد فالوورها
- فرمت هوشمند فالوورها (K, M, B)
- Infinite Scroll (بارگذاری خودکار با اسکرول)
- جستجوی Debounced
- Keyboard Navigation
- پشتیبانی کامل از API
- RTL Support
چه زمانی استفاده کنیم:
- وقتی فیلد جستجو مخصوص انتخاب کاربر است و نیاز به نمایش آواتار، نام و فالوور دارید
- وقتی لیست کاربران بزرگ است و نیاز به Infinite Scroll دارید
- وقتی جستجوی کاربران از API سرور انجام میشود
چه زمانی استفاده نکنیم:
- برای جستجوی موجودیتهای غیر کاربر — از
Autocompleteاستفاده کنید - برای انتخاب کاربر از لیست کوچک و ثابت — از
Selectاستفاده کنید
استفاده پایه
با API و Infinite Scroll
مثال کامل با API و بارگذاری صفحات بیشتر:
مثال ساده (بدون API)
برای دادههای استاتیک:
نسخه LTR
Props
UserAutocomplete
UserItem
interface UserItem {
id: string // شناسه یکتا
name: string // نام کاربر
username: string // نام کاربری
avatar?: string // آدرس تصویر پروفایل
followers: number // تعداد فالوورها
[key: string]: unknown // فیلدهای اضافی
}موارد استفاده
جستجوی کاربران با API
import { UserAutocomplete, UserItem } from '@parto-system-design/ui'
import { useState } from 'react'
export function UserSearch() {
const [value, setValue] = useState('')
const [users, setUsers] = useState<UserItem[]>([])
const [isLoading, setIsLoading] = useState(false)
const [isLoadingMore, setIsLoadingMore] = useState(false)
const [hasMore, setHasMore] = useState(true)
const [page, setPage] = useState(1)
const handleSearch = async (query: string, pageNum: number) => {
if (pageNum === 1) {
setIsLoading(true)
setUsers([])
setPage(1)
} else {
setIsLoadingMore(true)
}
try {
const response = await fetch(`/api/users/search?q=${query}&page=${pageNum}&pageSize=10`)
const data = await response.json()
if (pageNum === 1) {
setUsers(data.users)
} else {
setUsers((prev) => [...prev, ...data.users])
}
setHasMore(data.hasMore)
setPage(pageNum)
} catch (error) {
console.error(error)
} finally {
setIsLoading(false)
setIsLoadingMore(false)
}
}
const handleLoadMore = async () => {
await handleSearch(value, page + 1)
}
return (
<UserAutocomplete
value={value}
onValueChange={setValue}
users={users}
isLoading={isLoading}
isLoadingMore={isLoadingMore}
hasMore={hasMore}
onSearch={handleSearch}
onLoadMore={handleLoadMore}
placeholder="جستجوی کاربر..."
dir="rtl"
enableInfiniteScroll={true}
onSelect={(user) => {
console.log('Selected user:', user)
}}
/>
)
}استفاده ساده (بدون API)
import { UserAutocomplete } from '@parto-system-design/ui'
import { useState } from 'react'
const mockUsers = [
{
id: '1',
name: 'محسن یگانه',
username: 'mohsene',
followers: 7864554,
avatar: '/avatars/mohsen.jpg',
},
{
id: '2',
name: 'علی کریمی',
username: 'alikarimi8',
followers: 4605123,
avatar: '/avatars/ali.jpg',
},
]
export function SimpleUserSearch() {
const [value, setValue] = useState('')
return (
<UserAutocomplete
value={value}
onValueChange={setValue}
users={mockUsers}
placeholder="جستجوی کاربر..."
minChars={0}
dir="rtl"
enableInfiniteScroll={false}
/>
)
}فیلتر کردن کاربران Local
import { UserAutocomplete } from '@parto-system-design/ui'
import { useState } from 'react'
export function FilterUsers() {
const [value, setValue] = useState('')
const [filteredUsers, setFilteredUsers] = useState([])
const allUsers = [
// ... لیست کاربران
]
const handleSearch = (query: string) => {
const filtered = allUsers.filter((user) => user.name.includes(query) || user.username.includes(query))
setFilteredUsers(filtered)
}
return (
<UserAutocomplete
value={value}
onValueChange={setValue}
users={filteredUsers}
onSearch={handleSearch}
placeholder="جستجوی کاربر..."
dir="rtl"
enableInfiniteScroll={false}
/>
)
}انتخاب چندین کاربر
import { UserAutocomplete, UserItem } from '@parto-system-design/ui'
import { useState } from 'react'
export function MultiUserSelect() {
const [value, setValue] = useState('')
const [selectedUsers, setSelectedUsers] = useState<UserItem[]>([])
return (
<div className="space-y-4" dir="rtl">
<UserAutocomplete
value={value}
onValueChange={setValue}
// ... other props
clearOnSelect={true}
onSelect={(user) => {
setSelectedUsers((prev) => [...prev, user])
}}
/>
{/* نمایش کاربران انتخاب شده */}
<div className="flex flex-wrap gap-2">
{selectedUsers.map((user) => (
<div key={user.id} className="flex items-center gap-2 bg-accent px-3 py-1 rounded-full">
<img src={user.avatar} className="w-6 h-6 rounded-full" />
<span className="text-sm">{user.name}</span>
<button onClick={() => setSelectedUsers((prev) => prev.filter((u) => u.id !== user.id))}>×</button>
</div>
))}
</div>
</div>
)
}Infinite Scroll چگونه کار میکند؟
کامپوننت از Intersection Observer API برای شناسایی زمانی که کاربر به انتهای لیست رسیده استفاده میکند:
- وقتی کاربر اسکرول میکند و به انتهای لیست میرسد
- تابع
onLoadMoreفراخوانی میشود isLoadingMoreفعال میشود و Spinner نمایش داده میشود- صفحه بعدی از API بارگذاری میشود
- کاربران جدید به لیست فعلی اضافه میشوند
const handleLoadMore = async () => {
const nextPage = page + 1
const response = await fetch(`/api/users?page=${nextPage}`)
const data = await response.json()
// اضافه کردن کاربران جدید
setUsers((prev) => [...prev, ...data.users])
setPage(nextPage)
setHasMore(data.hasMore)
}فرمت کردن تعداد فالوورها
کامپوننت به طور خودکار فالوورها را فرمت میکند:
| تعداد | نمایش |
|---|---|
| 850 | 850 |
| 1,200 | 1.2K |
| 45,000 | 45K |
| 1,500,000 | 1.5M |
| 2,300,000,000 | 2.3B |
Keyboard Shortcuts
| کلید | عملکرد |
|---|---|
| ↓ Arrow Down | حرکت به کاربر بعدی |
| ↑ Arrow Up | حرکت به کاربر قبلی |
| Enter | انتخاب کاربر فعال |
| Escape | بستن منوی نتایج |
حالتها و انواع
با API و Infinite Scroll
حالت پیشفرض که enableInfiniteScroll={true} دارد و هنگام رسیدن به انتهای لیست، onLoadMore فراخوانی میشود.
بدون API (داده استاتیک)
با enableInfiniteScroll={false} و ارسال مستقیم آرایه کاربران.
پاکسازی پس از انتخاب
با clearOnSelect={true} برای سناریوهایی مانند انتخاب چندین کاربر پشت سر هم.
راهنمای استفاده
بکنید
- برای Infinite Scroll، حتماً
hasMoreوisLoadingMoreرا بهدرستی مدیریت کنید - ازdebounceDelayمناسب استفاده کنید تا درخواستهای اضافی به سرور ارسال نشود -onSearchرا طوری پیادهسازی کنید که همqueryو همpageرا مدیریت کند
نکنید
- از این کامپوننت برای جستجوی موجودیتهای غیر کاربر استفاده نکنید —
Autocompleteمناسبتر است - بدون مدیریتhasMore، بارگذاری بینهایت ایجاد نکنید -enableInfiniteScrollرا بدون تعریفonLoadMoreفعال نکنید
دسترسیپذیری
- پیمایش بین کاربران با کلیدهای ↑ و ↓
- انتخاب کاربر فعال با Enter
- بستن منوی نتایج با Escape
- تبدیل خودکار اعداد فالوور به فرمت فارسی
- پشتیبانی کامل از RTL و LTR
کامپوننتهای مرتبط
- اگر نیاز به جستجوی موجودیتهای غیر کاربر دارید → Autocomplete
- اگر نیاز به انتخاب چندین کاربر از لیست ثابت دارید → MultiSelect
- اگر فقط نیاز به انتخاب یک کاربر از لیست کوچک دارید → Select