# Paginators

> Show long lists one page at a time, with Next and Previous buttons.

A **paginator** shows pages one at a time. Members move between them with buttons. Use it for leaderboards, help menus, shop lists and image galleries.

Use `PaginatorV2`. The older `Paginator` still works for old scripts.

## Limits

| Thing | Limit |
|---|---|
| Paginators per run | 3 |
| Pages per paginator | 200 |
| Custom buttons per paginator | 20 |
| `timeout` | 1 to 3600 seconds |

## A full example

```python
p = PaginatorV2(interaction=interaction, show_first=True, show_last=True)

members = sorted(server.get_members().values(), key=lambda m: m.joined_at)
for start in range(0, len(members), 10):
    chunk = members[start:start + 10]
    lines = listify([m.display_name for m in chunk], prefix="number")
    p.add_page(Page(content=f"**Oldest members** (page %current_page%/%total_page%)\n{lines}"))

await p.start()
```

> **Keep the last page on screen:** When the paginator ends, the command's own reply replaces it. Leave the **Response** box empty to keep the last page showing.

## PaginatorV2

Makes a paginator.

```python
PaginatorV2(
    interaction=None,
    user_id=None,
    public=False,
    timeout=180,
    prev_text="Prev",
    next_text="Next",
    stop_text="Stop",
    first_text="First",
    last_text="Last",
    jump_text="Jump",
    auto_emojis=True,
    emoji_only=False,
    show_first=False,
    show_last=False,
    show_jump=False,
    show_stop=True,
)
```

| Argument | What it does |
|---|---|
| `interaction` | Pass `interaction` so the pages show in the command's reply. |
| `user_id` | Who can press the buttons. Default is the member who ran the command. |
| `public` | `True` lets anyone press the buttons. |
| `timeout` | Seconds with no clicks before the paginator ends. |
| `prev_text` … `jump_text` | The text on each button. |
| `prev_emoji` … `jump_emoji` | An emoji on each button. |
| `auto_emojis` | Add arrow emojis to the buttons. |
| `emoji_only` | Show only emojis, no text. |
| `show_first`, `show_last` | Show the **First** and **Last** buttons. |
| `show_jump` | Show a **Jump** button to go to any page. |
| `show_stop` | Show the **Stop** button. |

Others who press the buttons get *"This paginator belongs to someone else."*

## add_page

Adds a page.

```python
p.add_page(page)
p.add_page(build_function, *args)
```

A page can be text, an embed, a `Message(...)` or a `Page(...)`.

`%current_page%` and `%total_page%` in the page text become the page numbers.

### Lazy pages

Pass a **function** instead of a page. The function runs the first time someone opens that page, not before. It gets the extra arguments you pass. Its result is kept, so a page is built only once.

Use this when each page does heavy work, like drawing an image. Pages nobody opens are never built.

```python
async def card_page(member):
    img = await render_template("profile-card", name=member.display_name)
    return Page(content=member.mention, attachments=[img])

for m in list_members("Staff", as_mention=False):
    p.add_page(card_page, m)
```

The function can be normal or `async`.

## Page

Holds what one page shows.

```python
Page(content=None, embed=None, attachments=[], message=None)
```

| Argument | What it does |
|---|---|
| `content` | The text. |
| `embed` | One embed. |
| `attachments` | A list of files or `https://` links. |
| `message` | A `Message(...)` with text and an embed. |

## add_button

Adds a button of your own.

```python
p.add_button(
    label,
    style="secondary",
    action="next",
    emoji=None,
    url=None,
    row=2,
    on_click=None,
)
```

| Argument | What it does |
|---|---|
| `action` | `first`, `prev`, `next`, `last`, `stop`, `jump` or `reload`. |
| `url` | Makes it a link button. |
| `on_click` | Your own function instead of an action. It gets `(paginator, interaction)`. |

A wrong `action` stops the script and lists the valid ones.

```python
p.add_button("Back to start", action="first")
p.add_button("Help", url="https://example.com/help")
```

## start

Sends the first page. Needs `await`.

```python
await p.start(channel=None, block=True)
```

| Argument | What it does |
|---|---|
| `channel` | Send the pages to this channel instead of the command's reply. |
| `block` | `True`: the script waits until the paginator ends. `False`: the script goes on and the buttons keep working. |

With `block=True`, returns `"stopped"` or `"timeout"`.

## wait

Waits until the paginator ends. Returns `"stopped"` or `"timeout"`. Needs `await`.

```python
await p.wait()
```

## stop

Ends the paginator. Needs `await`.

```python
await p.stop()
```

## Paginator (old)

The first paginator. It blocks until it ends and has fewer options. Keep it for old scripts only.

```python
p = Paginator(interaction=interaction, user_id=None, timeout=60,
              prev_text="Prev", next_text="Next", stop_text="Stop")
p.add_page(page)
p.add_button(label, style, action="next")
```

Its `action` can be `first`, `prev`, `next`, `last` or `stop`.
