Navigation
Cursor Pagination
Курсорная навигация: диапазон строк вместо номеров страниц — он остаётся верным, когда данные меняются на ходу.
- pagination
- cursor
- list
- range
Превью
1440pxHost theme
Использовать с AI
- 1. Скопируйте ссылку.
- 2. Напишите агенту своими словами и вставьте её в предложение.
- 3. Агент откроет ссылку и поставит компонент из registry.
размести это в шапке: https://vibeui.ru/c/pagination-002
Показать полную инструкцию
То, что лежит по ссылке в развёрнутом виде. Нужна, если агент не может открыть ссылку — тогда вставьте этот текст целиком.
# Установка и размещение «pagination-002» (Cursor Pagination) из VibeUI
## 1. Сначала установка — не пропускай, не пересоздавай
Выполни эту команду до того, как писать код:
npx shadcn@latest add https://vibeui.ru/r/pagination-002.json
Registry item: https://vibeui.ru/r/pagination-002.json
Ставится в: components/vibeui/pagination-002.tsx (точный путь следует алиасам components.json этого проекта).
npm-зависимости: нет.
Registry-зависимости: нет.
Ставь из реестра. Не пересоздавай по описанию, не подменяй похожим
компонентом из другой библиотеки и не переписывай под стиль проекта.
## 2. Что это
Курсорная навигация: диапазон строк вместо номеров страниц — он остаётся верным, когда данные меняются на ходу.
Курсорная навигация: диапазон строк слева, кнопки «Назад» и «Вперёд» справа, край списка приглушён. Ноль зависимостей, один файл, клиентского JS нет.
## 3. Как использовать
import { Pagination002 } from "@/components/vibeui/pagination-002"
<Pagination002 from={41} perPage={20} total={214} />
Полный список пропсов — в установленном файле.
## 4. Куда поставить
Это мелкий inline-компонент. Поставь его ровно туда, куда просил
пользователь, внутрь существующей разметки. Не создавай ради него
новую страницу, секцию или обёртку. Если на этом месте уже стоит похожий
контрол — замени его, а не добавляй второй.
Куда поставить: ___
(Эту строку заполняет пользователь. Если она осталась пустой — спроси,
а не угадывай.)
## 5. Сохранить как установлено
- локальную палитру --vibeui-pagination-002-* и не подменять её токенами вашей темы
- счёт по строкам, а не по страницам: при меняющихся данных номер страницы врёт
- aria-disabled на краю списка вместо удаления кнопки — иначе строка прыгает
- табличные цифры в диапазоне
- ссылки, а не кнопки: адрес страницы должен открываться в новой вкладке
- стрелки из бордюров: они поворачиваются вместе с направлением
## 6. Можно менять
- from, perPage и total
- prevHref и nextHref
- акцент через проп accent
- ширину строки
## 7. Правила
- Компонент только показывает границы: курсор и запрос держит вызывающий код.
- Номеров страниц нет намеренно: для стабильных данных с переходом на страницу берите pagination-001.
- pointer-events:none на краю не отменяет переход по клавиатуре — уберите href, если нужен полный запрет.
- Не выносите CSS-переменные в globals.css: компонент обязан оставаться одним файлом.
## 8. Проверка
- рендерится без ошибок в консоли;
- выглядит так же, как превью на странице VibeUI, откуда взята ссылка;
- если нет — ты изменил что-то из раздела 5, верни как было.Для разработчика
npx shadcn@latest add https://vibeui.ru/r/pagination-002.jsonhttps://vibeui.ru/r/pagination-002.jsonКомпонент самодостаточен: один файл, без зависимостей, собственная палитра в локальных переменных --vibeui-pagination-002-*. Серверный: хуков нет, границы считаются из пропсов. Край списка помечен aria-disabled на ссылке: кнопка остаётся на месте, но не работает, поэтому строка навигации не прыгает. Числа набраны табличными цифрами.
Исходник компонента
Тот же файл, который поставит агент. Нужен, если вы предпочитаете скопировать код руками.
import type { ComponentPropsWithoutRef, CSSProperties } from "react"
export type Pagination002Props = Omit<
ComponentPropsWithoutRef<"nav">,
"children"
> & {
/** Номер первой строки на экране: счёт ведут по строкам, а не по страницам. */
from?: number
perPage?: number
total?: number
prevHref?: string
nextHref?: string
accent?: string
}
// Идея компонента: курсорная навигация для длинных списков. Номеров страниц
// нет намеренно: при постоянно меняющихся данных страница №7 завтра покажет
// другие строки, а «строки 41–60 из 214» остаётся правдой. Край списка
// закрывает недоступную кнопку через aria-disabled на ссылке: убирать её
// нельзя, иначе строка навигации прыгает.
const STYLES = `
:where([data-vibeui-block="pagination-002"]){
--vibeui-pagination-002-bg:oklch(1 0 0);
--vibeui-pagination-002-fg:oklch(0.24 0.014 265);
--vibeui-pagination-002-muted:oklch(0.56 0.014 265);
--vibeui-pagination-002-border:oklch(0.9 0.006 265);
--vibeui-pagination-002-hover:oklch(0.55 0.02 265 / 8%);
--vibeui-pagination-002-accent:oklch(0.55 0.2 262);
--vibeui-pagination-002-font:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
}
[data-vibeui-block="pagination-002"]{
display:flex;align-items:center;justify-content:space-between;gap:1rem;
width:100%;max-width:26rem;box-sizing:border-box;padding:0.5rem 0.625rem;
background:var(--vibeui-pagination-002-bg);
border:1px solid var(--vibeui-pagination-002-border);border-radius:0.75rem;
font-family:var(--vibeui-pagination-002-font);color:var(--vibeui-pagination-002-fg);
}
/* Счёт по строкам: он остаётся верным, даже когда данные меняются. */
[data-vibeui-block="pagination-002"] [data-part="range"]{
font-size:0.8125rem;color:var(--vibeui-pagination-002-muted);
font-variant-numeric:tabular-nums;
}
[data-vibeui-block="pagination-002"] [data-part="range"] b{color:var(--vibeui-pagination-002-fg);font-weight:650}
[data-vibeui-block="pagination-002"] [data-part="side"]{display:flex;align-items:center;gap:0.375rem}
[data-vibeui-block="pagination-002"] [data-part="step"]{
display:inline-flex;align-items:center;gap:0.375rem;
height:2rem;padding:0 0.625rem;border-radius:0.5rem;
border:1px solid var(--vibeui-pagination-002-border);
color:inherit;text-decoration:none;font-size:0.8125rem;
}
[data-vibeui-block="pagination-002"] [data-part="step"]:hover{background:var(--vibeui-pagination-002-hover)}
[data-vibeui-block="pagination-002"] [data-part="step"]:focus-visible{outline:2px solid var(--vibeui-pagination-002-accent);outline-offset:2px}
/* Край списка: кнопка остаётся на месте, но не работает — строка не прыгает. */
[data-vibeui-block="pagination-002"] [data-part="step"][aria-disabled="true"]{
color:var(--vibeui-pagination-002-muted);pointer-events:none;opacity:.55;
}
[data-vibeui-block="pagination-002"] [data-part="arrow"]{
width:0.375rem;height:0.375rem;
border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;
}
[data-vibeui-block="pagination-002"] [data-part="step"][data-dir="prev"] [data-part="arrow"]{transform:rotate(135deg)}
[data-vibeui-block="pagination-002"] [data-part="step"][data-dir="next"] [data-part="arrow"]{transform:rotate(-45deg)}
@media (prefers-reduced-motion:reduce){[data-vibeui-block="pagination-002"] *{animation:none!important;transition:none!important}}
`
/**
* Курсорная навигация: диапазон строк вместо номеров страниц.
* Один файл, ноль зависимостей, собственная палитра.
*/
export function Pagination002({
from = 41,
perPage = 20,
total = 214,
prevHref = "#",
nextHref = "#",
accent,
className,
style,
...props
}: Pagination002Props) {
const to = Math.min(from + perPage - 1, total)
const atStart = from <= 1
const atEnd = to >= total
const palette = {
...(accent ? { "--vibeui-pagination-002-accent": accent } : null),
...style,
} as CSSProperties
return (
<>
<style href="vibeui-pagination-002" precedence="medium">
{STYLES}
</style>
<nav
{...props}
data-vibeui-block="pagination-002"
aria-label="Навигация по списку"
className={className}
style={palette}
>
<p data-part="range">
Строки{" "}
<b>
{from}–{to}
</b>{" "}
из {total}
</p>
<div data-part="side">
<a
data-part="step"
data-dir="prev"
href={prevHref}
aria-disabled={atStart || undefined}
>
<span data-part="arrow" aria-hidden="true" />
Назад
</a>
<a
data-part="step"
data-dir="next"
href={nextHref}
aria-disabled={atEnd || undefined}
>
Вперёд
<span data-part="arrow" aria-hidden="true" />
</a>
</div>
</nav>
</>
)
}