blackcat.js-discord - v1.0.10
    Preparing search index...

    Type Alias PaginationOptions<T>

    Cấu hình cho PaginationBuilder

    Pagination hỗ trợ 2 chế độ dữ liệu (data source):

    • Cung cấp toàn bộ dữ liệu ngay từ đầu
    • Phù hợp với dataset nhỏ
    • Load dữ liệu theo từng trang (lazy loading)
    • Phù hợp với API / database

    ⚠️ Chỉ nên dùng một trong hai:

    • pages hoặc pageLoader + totalPages
    // Dữ liệu tĩnh
    const options: PaginationOptions<string> = {
    pages: ["A", "B", "C"],
    renderPage: ({ data }) => ({ content: data })
    };
    // Dữ liệu động
    const options: PaginationOptions<string[]> = {
    totalPages: 5,
    pageLoader: async (page) => {
    return [`Item ${page}`];
    },
    renderPage: ({ data }) => ({
    content: data.join("\n")
    })
    };
    type PaginationOptions<T> = {
        buildComponents?: (
            ctx: {
                components: ComponentBuilder<AnyComponent>;
                data: T;
                page: number;
                pagination: PaginationBuilder<T>;
                totalPages: number;
            },
        ) => | ComponentBuilder<AnyComponent>
        | Promise<ComponentBuilder<AnyComponent>>;
        interactionFilter?: (
            interaction: ButtonInteraction | StringSelectMenuInteraction,
        ) => boolean;
        navigation?: {
            first?: boolean;
            jump?: boolean;
            last?: boolean;
            menu?: boolean;
            next?: boolean;
            prev?: boolean;
        };
        onComponent?: (
            ctx: {
                data: T;
                interaction: ButtonInteraction | StringSelectMenuInteraction;
                page: number;
                pagination: PaginationBuilder<T>;
                totalPages: number;
            },
        ) => Promise<any>
        | any;
        onError?: (
            ctx: { error: unknown; pagination: PaginationBuilder<T> },
        ) => any;
        onPageChange?: (
            ctx: {
                data: T;
                interaction: ButtonInteraction | StringSelectMenuInteraction;
                newPage: number;
                oldPage: number;
                pagination: PaginationBuilder<T>;
            },
        ) => Promise<any>
        | any;
        onStart?: (
            ctx: { data: T; message: Message; pagination: PaginationBuilder<T> },
        ) => any;
        onStop?: (
            ctx: { data: T; message: Message; pagination: PaginationBuilder<T> },
        ) => any;
        onTimeout?: (
            ctx: { data: T; message: Message; pagination: PaginationBuilder<T> },
        ) => any;
        pageLoader?: (page: number) => Promise<T>;
        pages?: T[];
        renderPage:
            | ((ctx: PaginationRenderContext<T>) => Promise<BaseMessageOptions>)
            | ((ctx: PaginationRenderContext<T>) => BaseMessageOptions);
        selectMenu?: {
            enabled?: boolean;
            pageLabel?: (page: number) => string;
            placeholder?: string;
        };
        timeout?: number;
        totalPages?: number;
        useCache?: boolean;
    }

    Type Parameters

    • T

      Kiểu dữ liệu của mỗi trang

    Index

    Properties

    buildComponents?: (
        ctx: {
            components: ComponentBuilder<AnyComponent>;
            data: T;
            page: number;
            pagination: PaginationBuilder<T>;
            totalPages: number;
        },
    ) => | ComponentBuilder<AnyComponent>
    | Promise<ComponentBuilder<AnyComponent>>

    Tuỳ chỉnh components (buttons, select menu)

    Type Declaration

      • (
            ctx: {
                components: ComponentBuilder<AnyComponent>;
                data: T;
                page: number;
                pagination: PaginationBuilder<T>;
                totalPages: number;
            },
        ): ComponentBuilder<AnyComponent>
        | Promise<ComponentBuilder<AnyComponent>>
      • Parameters

        • ctx: {
              components: ComponentBuilder<AnyComponent>;
              data: T;
              page: number;
              pagination: PaginationBuilder<T>;
              totalPages: number;
          }

          Context build components

          Dùng khi:

          • Muốn thêm button riêng
          • Thêm action custom
          • components: ComponentBuilder<AnyComponent>

            Builder dùng để tạo và chỉnh sửa components.

            Đây là object mutable:

            • Có thể thêm button, select menu, ...
            • Sau khi chỉnh sửa, cần return lại để áp dụng
            components.addButton([
            { customId: "next", label: "Next", style: "Primary" }
            ]);
            return components;
          • data: T

            Dữ liệu của trang hiện tại.

            Đây là kết quả từ PaginationBuilder.getPageData. Có thể là array, object, hoặc bất kỳ kiểu dữ liệu generic T.

            // Nếu T là array
            data.forEach(item => ...)
          • page: number

            Trang hiện tại (bắt đầu từ 1).

            Dùng để:

            • Disable/enable nút phân trang (prev/next)
            • Hiển thị trạng thái hiện tại
            disabled: page <= 1
            
          • pagination: PaginationBuilder<T>

            Instance hiện tại của PaginationBuilder.

            Cho phép truy cập:

            • pagination.state (state tuỳ chỉnh)
            • Các method như reload, refreshComponents, ...

            ⚠️ Tránh gọi reload() bên trong buildComponents để không gây loop không mong muốn.

          • totalPages: number

            Tổng số trang có sẵn.

            Dùng để:

            • Giới hạn pagination
            • Kiểm tra trang cuối
            disabled: page >= totalPages
            

        Returns ComponentBuilder<AnyComponent> | Promise<ComponentBuilder<AnyComponent>>

    // Custom thêm button
    const pagination = new PaginationBuilder<string[]>({
    pages: ["Trang 1: Hello", "Trang 2: World", "Trang 3: Pagination"],
    renderPage: ({ data }) => ({
    content: data
    }),
    buildComponents: ({ components }) => {
    components.addButton([
    {
    customId: "custom:hello",
    label: "Hello",
    style: "Success"
    }
    ]);
    return components;
    }
    });
    interactionFilter?: (
        interaction: ButtonInteraction | StringSelectMenuInteraction,
    ) => boolean

    Filter interaction

    Dùng để:

    • Chỉ cho phép user cụ thể sử dụng
    • Chặn người khác
    interactionFilter: (i) => i.user.id === message.author.id
    // hoặc
    interactionFilter: (interaction) => {
    if (interaction.user.id !== message.author.id) {
    interaction.reply({
    content: "❌ Chỉ người dùng lệnh mới có thể sử dụng.",
    flags: MessageFlags.Ephemeral
    });
    return false;
    }
    return true;
    },
    navigation?: {
        first?: boolean;
        jump?: boolean;
        last?: boolean;
        menu?: boolean;
        next?: boolean;
        prev?: boolean;
    }

    Cấu hình navigation buttons

    Bật/tắt các nút:

    • first, prev, next, last
    • jump (modal)
    • menu (select menu)
    navigation: {
    first: true,
    prev: true,
    next: true,
    last: true,
    jump: false,
    menu: true
    }
    onComponent?: (
        ctx: {
            data: T;
            interaction: ButtonInteraction | StringSelectMenuInteraction;
            page: number;
            pagination: PaginationBuilder<T>;
            totalPages: number;
        },
    ) => Promise<any>
    | any

    Xử lý interaction custom (ngoài navigation mặc định)

    Type Declaration

      • (
            ctx: {
                data: T;
                interaction: ButtonInteraction | StringSelectMenuInteraction;
                page: number;
                pagination: PaginationBuilder<T>;
                totalPages: number;
            },
        ): Promise<any>
        | any
      • Parameters

        • ctx: {
              data: T;
              interaction: ButtonInteraction | StringSelectMenuInteraction;
              page: number;
              pagination: PaginationBuilder<T>;
              totalPages: number;
          }

          Context interaction

          • data: T

            Data của trang hiện tại

            Đây là kết quả từ PaginationBuilder.getPageData

            Có thể là:

            • Array
            • Object
            • Bất kỳ kiểu generic T
            // Nếu T là array
            data.map(item => ...)
          • interaction: ButtonInteraction | StringSelectMenuInteraction

            Interaction từ button hoặc select menu

            Có thể dùng để:

            • Kiểm tra customId
            • Reply / defer / update interaction
            if (interaction.customId === "custom:hello") { ... }
            
          • page: number

            Trang hiện tại (bắt đầu từ 1)

            Hữu ích khi:

            • Xử lý logic theo page
            • Kiểm tra điều kiện phân trang
          • pagination: PaginationBuilder<T>

            Instance pagination

            Cho phép:

            • Truy cập pagination.state
            • Gọi các method như reload, refreshComponents
          • totalPages: number

            Tổng số trang

            Dùng để:

            • Giới hạn logic theo page
            • Kiểm tra trang cuối

        Returns Promise<any> | any

    Được gọi khi user tương tác với:

    • Button custom
    • Select menu custom

    Không xử lý các action mặc định như next/prev nếu bạn không override.

    Dùng khi:

    • Bạn có button riêng
    • Cần handle logic riêng cho từng interaction
    • Muốn trigger action như reply, update, reload pagination
    // Handle button custom
    const pagination = new PaginationBuilder<string[]>({
    pages: ["Trang 1", "Trang 2"],
    renderPage: ({ data }) => ({ content: data }),
    buildComponents: ({ components }) => {
    components.addButton([
    {
    customId: "custom:hello",
    label: "Hello",
    style: "Success"
    }
    ]);
    return components;
    },
    onComponent: async ({ interaction }) => {
    if (interaction.customId === "custom:hello") {
    await interaction.reply("Hello!");
    }
    }
    });
    onError?: (ctx: { error: unknown; pagination: PaginationBuilder<T> }) => any

    Callback xử lý lỗi toàn cục.

    Được gọi khi có lỗi xảy ra trong lifecycle của pagination.

    Dùng để:

    • Log lỗi
    • Tránh crash ứng dụng
    • Gửi thông báo cho developer / user

    ⚠️ Nếu không cung cấp hook này, lỗi có thể bị throw ra ngoài.

    onError: ({ error }) => {
    console.error(error);
    }
    // Logging nâng cao
    onError: async ({ error }) => {
    await logToService(error);
    }

    PaginationBuilder

    onPageChange?: (
        ctx: {
            data: T;
            interaction: ButtonInteraction | StringSelectMenuInteraction;
            newPage: number;
            oldPage: number;
            pagination: PaginationBuilder<T>;
        },
    ) => Promise<any>
    | any

    Callback được gọi mỗi khi page thay đổi.

    Type Declaration

      • (
            ctx: {
                data: T;
                interaction: ButtonInteraction | StringSelectMenuInteraction;
                newPage: number;
                oldPage: number;
                pagination: PaginationBuilder<T>;
            },
        ): Promise<any>
        | any
      • Parameters

        • ctx: {
              data: T;
              interaction: ButtonInteraction | StringSelectMenuInteraction;
              newPage: number;
              oldPage: number;
              pagination: PaginationBuilder<T>;
          }

          OnPageChangeContext

          • data: T

            Dữ liệu của trang hiện tại.

            Đây là kết quả từ PaginationBuilder.getPageData. Có thể là array, object, hoặc bất kỳ kiểu dữ liệu generic T.

            // Nếu T là array
            data.forEach(item => ...)
          • interaction: ButtonInteraction | StringSelectMenuInteraction

            Interaction đã trigger việc đổi trang.

            Có thể là:

            • ButtonInteraction (prev/next)
            • StringSelectMenuInteraction (jump page)

            Dùng để:

            • Reply / defer
            • Lấy user info
            await interaction.deferUpdate();
            
          • newPage: number

            Trang mới sau khi thay đổi.

            Giá trị page sau khi user tương tác (button/select menu).

            if (newPage === totalPages) console.log("Đến trang cuối");
            
          • oldPage: number

            Trang trước đó.

            Giá trị page trước khi pagination được cập nhật.

            if (oldPage === 1) console.log("Rời trang đầu");
            
          • pagination: PaginationBuilder<T>

            Instance hiện tại của PaginationBuilder.

            Có thể dùng để:

            • Truy cập state (pagination.state)
            • Gọi các method như reload, refreshComponents

            ⚠️ Tránh gọi lại logic đổi page bên trong hook này để không gây loop ngoài ý muốn.

        Returns Promise<any> | any

        Có thể trả về Promise hoặc giá trị bất kỳ (không được sử dụng nội bộ)

    Hook này chạy sau khi page đã được cập nhật, thường được dùng để:

    • Log / analytics
    • Sync state bên ngoài
    • Trigger side-effects

    ⚠️ Lưu ý:

    • Không nên mutate lại pagination.page trong hook này
    • Tránh gọi lại action đổi page → có thể gây loop
    onPageChange: ({ oldPage, newPage }) => {
    console.log(`Page: ${oldPage} -> ${newPage}`);
    }
    // Tracking user interaction
    onPageChange: async ({ interaction, newPage }) => {
    await logToDatabase({
    userId: interaction.user.id,
    page: newPage
    });
    }
    // Sync state ngoài
    onPageChange: ({ pagination, newPage }) => {
    pagination.state.lastVisitedPage = newPage;
    }

    PaginationBuilder

    onStart?: (
        ctx: { data: T; message: Message; pagination: PaginationBuilder<T> },
    ) => any

    Callback được gọi khi pagination bắt đầu.

    Type Declaration

      • (ctx: { data: T; message: Message; pagination: PaginationBuilder<T> }): any
      • Parameters

        • ctx: { data: T; message: Message; pagination: PaginationBuilder<T> }
          • data: T

            Dữ liệu của trang hiện tại.

            Đây là kết quả từ PaginationBuilder.getPageData. Có thể là array, object, hoặc bất kỳ kiểu dữ liệu generic T.

            // Nếu T là array
            data.forEach(item => ...)
          • message: Message

            Message đã được gửi và gắn với pagination.

            Đây là message chính mà pagination sẽ thao tác (edit, update components, ...).

            console.log(message.id);
            
          • pagination: PaginationBuilder<T>

            Instance hiện tại của PaginationBuilder.

            Cho phép truy cập:

            • pagination.state
            • Các method như reload, refreshComponents

        Returns any

        Có thể trả về Promise hoặc giá trị bất kỳ (không được sử dụng nội bộ)

    Hook này chạy sau khi message đã được gửi và pagination đã được khởi tạo hoàn chỉnh.

    Thường dùng để:

    • Log / debug
    • Lưu reference message
    • Trigger side-effects ban đầu

    ⚠️ Lưu ý:

    • Không nên edit message ngay trong hook này nếu không cần thiết
    • Tránh gọi reload() liên tục → có thể gây spam API
    onStart: ({ message }) => {
    console.log("Started:", message.id);
    }
    // Lưu message để dùng sau
    onStart: ({ message, pagination }) => {
    pagination.state.messageId = message.id;
    }
    // Logging async
    onStart: async ({ message }) => {
    await logToDatabase({ messageId: message.id });
    }

    PaginationBuilder

    onStop?: (
        ctx: { data: T; message: Message; pagination: PaginationBuilder<T> },
    ) => any

    Callback được gọi khi pagination dừng.

    Hook này chạy khi collector bị stop (thủ công hoặc do timeout).

    Thường dùng để:

    • Cleanup (disable buttons, xoá state, ...)
    • Log / debug

    ⚠️ Lưu ý:

    • Hook này luôn chạy, kể cả khi timeout
    • Nếu cần phân biệt timeout, dùng onTimeout
    onStop: ({ message }) => {
    console.log("Stopped:", message.id);
    }
    // Disable toàn bộ button khi stop
    onStop: async ({ pagination }) => {
    await pagination.stop();
    }

    onTimeout

    onTimeout?: (
        ctx: { data: T; message: Message; pagination: PaginationBuilder<T> },
    ) => any

    Callback được gọi khi pagination hết thời gian (timeout).

    Hook này chạy khi collector tự động stop do vượt quá timeout.

    Thường dùng để:

    • Thông báo timeout
    • Disable UI
    • Cleanup tài nguyên

    ⚠️ Lưu ý:

    • Hook này chỉ chạy khi timeout (không chạy khi stop thủ công)
    • Sau đó onStop vẫn sẽ được gọi
    onTimeout: () => {
    console.log("Timeout");
    }
    // Disable UI khi timeout
    onTimeout: async ({ pagination }) => {
    await pagination.stop();
    }

    onStop

    pageLoader?: (page: number) => Promise<T>

    Hàm bất đồng bộ dùng để load dữ liệu cho từng trang

    Type Declaration

      • (page: number): Promise<T>
      • Parameters

        • page: number

          Số trang cần load (0-based index)

        Returns Promise<T>

        Promise Dữ liệu tương ứng của trang

    Đây là nguồn dữ liệu động (dynamic data source) cho pagination.

    Khi sử dụng pageLoader:

    • Bạn bắt buộc phải cung cấp totalPages
    • Dữ liệu sẽ được load theo từng trang khi cần (lazy loading)
    • Có tích hợp cache nội bộ (trừ khi bị tắt)

    Dùng trong các trường hợp:

    • Dữ liệu lớn (database, API, phân trang server-side)
    • Không muốn load toàn bộ dữ liệu vào memory
    • Cần tối ưu performance và bandwidth
    • Khi chuyển trang → pageLoader(page) sẽ được gọi
    • Nếu đã load trước đó → có thể dùng cache (tuỳ cấu hình)
    • Dữ liệu trả về sẽ được truyền vào renderPage

    ⚠️ Lưu ý:

    • page0-based, không phải 1-based
    • Phải đảm bảo luôn trả về đúng kiểu T
    • Không nên throw error trực tiếp → nên handle hoặc dùng onError
    // Dùng với database
    pageLoader: async (page) => {
    return await db.getUsers({
    skip: page * 10,
    limit: 10
    });
    }
    // Dùng với REST API
    totalPages: 5,
    pageLoader: async (page) => {
    const res = await fetch(`/api/items?page=${page + 1}`);
    const json = await res.json();
    return json.data;
    }
    // Ví dụ mock data (test nhanh)
    totalPages: 5,
    pageLoader: async (page) => {
    await new Promise(r => setTimeout(r, 500)); // giả lập delay

    return Array.from({ length: 5 }, (_, i) => {
    return `Item ${page}-${i}`;
    });
    }
    • PaginationOptions.totalPages
    • PaginationBuilder.getPageData
    pages?: T[]

    Danh sách dữ liệu tĩnh cho từng trang

    Dùng khi bạn đã có sẵn toàn bộ dữ liệu. Pagination sẽ truy cập trực tiếp từ mảng này.

    ⚠️ Không nên dùng nếu dữ liệu lớn (nên dùng pageLoader)

    pages: ["Page 1", "Page 2", "Page 3"]
    
    renderPage:
        | ((ctx: PaginationRenderContext<T>) => Promise<BaseMessageOptions>)
        | ((ctx: PaginationRenderContext<T>) => BaseMessageOptions)

    Hàm render nội dung của trang

    Context render

    BaseMessageOptions

    Đây là function bắt buộc.

    Dùng để:

    • Tạo content (text, embed,...)
    • Hiển thị data của trang
    renderPage: ({ data, page, totalPages }) => ({
    content: `Trang ${page + 1}/${totalPages}\n${data}`
    })
    selectMenu?: {
        enabled?: boolean;
        pageLabel?: (page: number) => string;
        placeholder?: string;
    }

    Cấu hình select menu (dropdown) cho pagination.

    Type Declaration

    • Optionalenabled?: boolean

      Bật/tắt select menu.

      false
      

      Khi bật:

      • Hiển thị dropdown chứa danh sách các trang
      • Cho phép user nhảy đến trang bất kỳ
    • OptionalpageLabel?: (page: number) => string

      Hàm dùng để format label cho từng option (trang).

      Dùng để tuỳ chỉnh cách hiển thị trang.

      pageLabel: (page) => `Trang ${page}`
      
      // Hiển thị dạng fancy
      pageLabel: (page) => `📄 Page ${page}`
    • Optionalplaceholder?: string

      Nội dung placeholder của select menu.

      "Select a page"
      

      Text hiển thị khi chưa có option nào được chọn.

      placeholder: "Chọn trang..."
      

    Cho phép người dùng chọn trực tiếp trang thông qua dropdown. Nếu không cấu hình hoặc enabled = false, select menu sẽ không được render.

    selectMenu: {
    enabled: true,
    placeholder: "Chọn trang",
    pageLabel: (page) => `Trang ${page}`
    }
    timeout?: number

    Thời gian timeout của pagination (tính bằng giây).

    120
    

    Sau khoảng thời gian này:

    • Collector sẽ tự động stop
    • Trigger onTimeout
    • Sau đó trigger onStop
    timeout: 60 // 1 phút
    
    totalPages?: number

    Tổng số trang (dùng với pageLoader)

    Bắt buộc khi bạn dùng pageLoader.

    Vì pagination không biết trước số trang nếu load động.

    totalPages: 10
    
    useCache?: boolean

    Bật / tắt cơ chế cache dữ liệu page

    Tùy chọn này chỉ có hiệu lực khi bạn sử dụng pageLoader. Nếu bạn sử dụng pages (static data) thì cache sẽ không được dùng.

    false
    

    Khi useCache = true, dữ liệu của từng page sẽ được lưu lại sau lần load đầu tiên thông qua pageLoader, nhằm tránh việc gọi lại nhiều lần cho cùng một page.

    Cơ chế hoạt động:

    1. Khi gọi getPageData(page)
    2. Nếu page đã tồn tại trong pageCache → trả về ngay (cache hit)
    3. Nếu chưa có → gọi pageLoader(page) để lấy dữ liệu (cache miss)
    4. Sau đó lưu vào cache (nếu bật useCache)

    Phù hợp khi:

    • Dữ liệu load từ API / database (tốn tài nguyên)
    • Pagination có thể quay lại page cũ nhiều lần
    • Muốn tối ưu performance và giảm latency

    Không nên bật khi:

    • Dữ liệu thay đổi liên tục (real-time)
    • Cần luôn fetch dữ liệu mới nhất
    const pagination = new PaginationBuilder({
    pageLoader: async (page) => {
    return fetchData(page); // gọi API
    },
    useCache: true
    });

    // Lần đầu → gọi API
    await pagination.getPageData(0);

    // Lần sau → dùng cache (không gọi lại API)
    await pagination.getPageData(0);
    // Tắt cache (luôn gọi lại pageLoader)
    const pagination = new PaginationBuilder({
    pageLoader: async (page) => fetchData(page),
    useCache: false
    });

    getPageData