fix(ui): ограничить списки деталей OSPF без потери данных
This commit is contained in:
1 parent
8d86c5cbb0
commit
086f118204
9 files changed
+110
-11
No files matched your search
@@ -0,0 +1,33 @@
|
||||
"use client"
|
||||
|
||||
import { useMemo, useState, type ReactNode } from "react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
|
||||
export function OspfDetailList<T>({ items, label, getSearchValue, renderItem }: {
|
||||
items: T[]; label: string; getSearchValue: (item: T) => string
|
||||
renderItem: (item: T, index: number) => ReactNode
|
||||
}) {
|
||||
const [query, setQuery] = useState("")
|
||||
const [page, setPage] = useState(0)
|
||||
const filtered = useMemo(() => {
|
||||
const value = query.trim().toLowerCase()
|
||||
return value ? items.filter(item => getSearchValue(item).toLowerCase().includes(value)) : items
|
||||
}, [items, query, getSearchValue])
|
||||
const pages = Math.max(1, Math.ceil(filtered.length / 50))
|
||||
if (page >= pages) setPage(pages - 1)
|
||||
const current = Math.min(page, pages - 1)
|
||||
const controlled = items.length > 50 || !!query
|
||||
return <section aria-label={label} className="min-w-0">
|
||||
{controlled && <div className="flex min-w-0 flex-col gap-2 mb-2">
|
||||
<Input aria-label={`Поиск: ${label}`} placeholder="Поиск во всём списке…" value={query} onChange={event => { setQuery(event.target.value); setPage(0) }} />
|
||||
<p role="status" className="text-xs text-muted-foreground">Показаны {filtered.length ? current * 50 + 1 : 0}–{Math.min((current + 1) * 50, filtered.length)} из {filtered.length}. Всего {items.length}.</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button type="button" variant="outline" className="min-h-11" aria-label={`Предыдущая страница: ${label}`} disabled={!current} onClick={() => setPage(current - 1)}>Назад</Button>
|
||||
<Button type="button" variant="outline" className="min-h-11" aria-label={`Следующая страница: ${label}`} disabled={current >= pages - 1} onClick={() => setPage(current + 1)}>Далее</Button>
|
||||
</div>
|
||||
{!filtered.length && <p className="text-xs text-muted-foreground">По вашему запросу ничего не найдено.</p>}
|
||||
</div>}
|
||||
<div className="flex flex-col gap-0 divide-y divide-border">{filtered.slice(current * 50, (current + 1) * 50).map((item, index) => renderItem(item, current * 50 + index))}</div>
|
||||
</section>
|
||||
}
|
||||
Reference in new issue
Block a user