@gramio/pagination
Version: 
Pagination library for telegram bots
285 lines (280 loc) • 7.57 kB
JavaScript
'use strict';
var callbackData = require('@gramio/callback-data');
var keyboards = require('@gramio/keyboards');
function calculatePagination(count, offset, limit) {
  const totalPages = Math.ceil(count / limit);
  const currentPage = Math.floor(offset / limit) + 1;
  const hasNext = currentPage < totalPages;
  const hasPrevious = currentPage > 1;
  return {
    totalPages,
    currentPage,
    hasNext,
    hasPrevious
  };
}
class Pagination {
  name;
  getData;
  limitValue = 10;
  columnsValue;
  itemDataIterator;
  onSelectCallback;
  getCount;
  pageInfoFormat;
  firstLastPage = false;
  wrapKeyboardHandler;
  callbackData;
  selectCallbackDataFunction;
  payloadInstance;
  constructor(name, funcOrCallbackData, func) {
    this.name = name;
    this.getData = funcOrCallbackData instanceof callbackData.CallbackData ? func : funcOrCallbackData;
    this.payloadInstance = funcOrCallbackData instanceof callbackData.CallbackData ? funcOrCallbackData : void 0;
    const payloadCallbackData = this.payloadInstance ?? new callbackData.CallbackData("noop");
    this.callbackData = new callbackData.CallbackData(name).enum("type", ["set", "select", "set_page"]).number("offset").data("payload", payloadCallbackData, {
      optional: true
    });
  }
  payload(payload) {
    this.payloadInstance = payload;
    return this;
  }
  wrapKeyboard(func) {
    this.wrapKeyboardHandler = func;
    return this;
  }
  limit(count) {
    this.limitValue = count;
    return this;
  }
  count(func) {
    this.getCount = func;
    return this;
  }
  item(item) {
    this.itemDataIterator = item;
    return this;
  }
  columns(count) {
    this.columnsValue = count;
    return this;
  }
  onSelect(callback) {
    this.onSelectCallback = callback;
    return this;
  }
  selectCallbackData(callback) {
    this.selectCallbackDataFunction = callback;
    return this;
  }
  withPageInfo(format) {
    this.pageInfoFormat = format;
    return this;
  }
  withFirstLastPage() {
    this.firstLastPage = true;
    return this;
  }
  async getDataWithPaginationInfo(offset, ...args) {
    if (!this.getCount) {
      const data2 = await this.getData({
        offset,
        limit: this.limitValue + 1,
        payload: args[0]
      });
      return {
        data: data2.slice(0, this.limitValue),
        pagination: {
          hasNext: data2.length > this.limitValue,
          hasPrevious: offset > 0
        }
      };
    }
    const [count, data] = await Promise.all([
      this.getCount(),
      this.getData({
        offset,
        limit: this.limitValue,
        payload: args[0]
      })
    ]);
    return {
      data,
      pagination: calculatePagination(count, offset, this.limitValue)
    };
  }
  async getKeyboardWithData(offset = 0, ...args) {
    const { data, pagination } = await this.getDataWithPaginationInfo(
      offset,
      ...args
    );
    const keyboard = new keyboards.InlineKeyboard({
      enableSetterKeyboardHelpers: true
    }).columns(this.columnsValue).add(
      ...data.map((x) => {
        const item = this.itemDataIterator?.(x);
        return keyboards.InlineKeyboard.text(
          // @ts-expect-error
          item?.title ?? x.title,
          this.selectCallbackDataFunction?.({
            // @ts-expect-error
            id: item?.id ?? x.id,
            payload: args[0],
            offset,
            limit: this.limitValue
          }) ?? this.callbackData.pack({
            type: "select",
            // @ts-expect-error
            offset: item?.id ?? x.id,
            payload: args[0]
          })
        );
      })
    ).row().columns(void 0).addIf(
      this.firstLastPage && pagination.hasPrevious,
      keyboards.InlineKeyboard.text(
        "\u23EE\uFE0F",
        this.callbackData.pack({
          type: "set_page",
          offset: 0,
          payload: args[0]
        })
      )
    ).addIf(
      pagination.hasPrevious,
      keyboards.InlineKeyboard.text(
        "\u2B05\uFE0F",
        this.callbackData.pack({
          type: "set",
          offset: offset - this.limitValue,
          payload: args[0]
        })
      )
    ).addIf(
      "totalPages" in pagination && !!this.pageInfoFormat,
      keyboards.InlineKeyboard.text(
        // biome-ignore lint/style/noNonNullAssertion: <explanation>
        this.pageInfoFormat(pagination),
        "$noop$"
      )
    ).addIf(
      pagination.hasNext,
      keyboards.InlineKeyboard.text(
        "\u27A1\uFE0F",
        this.callbackData.pack({
          type: "set",
          offset: offset + this.limitValue,
          payload: args[0]
        })
      )
    ).addIf(
      this.firstLastPage && "totalPages" in pagination && pagination.hasNext,
      keyboards.InlineKeyboard.text(
        "\u23ED\uFE0F",
        this.callbackData.pack({
          type: "set_page",
          offset: pagination.totalPages - 1,
          payload: args[0]
        })
      )
    );
    return {
      keyboard: this.wrapKeyboardHandler?.({
        keyboard,
        pagination,
        offset,
        limit: this.limitValue,
        data,
        payload: args[0]
      }) ?? keyboard,
      data,
      pagination
    };
  }
  async getKeyboard(offset = 0, ...args) {
    const { data, pagination } = await this.getDataWithPaginationInfo(
      offset,
      ...args
    );
    const keyboard = new keyboards.InlineKeyboard({
      enableSetterKeyboardHelpers: true
    }).columns(this.columnsValue).add(
      ...data.map((x) => {
        const item = this.itemDataIterator?.(x);
        return keyboards.InlineKeyboard.text(
          // @ts-expect-error
          item?.title ?? x.title,
          this.selectCallbackDataFunction?.({
            // @ts-expect-error
            id: item?.id ?? x.id,
            payload: args[0],
            offset,
            limit: this.limitValue
          }) ?? this.callbackData.pack({
            type: "select",
            // @ts-expect-error
            offset: item?.id ?? x.id,
            payload: args[0]
          })
        );
      })
    ).row().columns(void 0).addIf(
      this.firstLastPage && pagination.hasPrevious,
      keyboards.InlineKeyboard.text(
        "\u23EE\uFE0F",
        this.callbackData.pack({
          type: "set_page",
          offset: 0,
          payload: args[0]
        })
      )
    ).addIf(
      pagination.hasPrevious,
      keyboards.InlineKeyboard.text(
        "\u2B05\uFE0F",
        this.callbackData.pack({
          type: "set",
          offset: offset - this.limitValue,
          payload: args[0]
        })
      )
    ).addIf(
      "totalPages" in pagination && !!this.pageInfoFormat,
      keyboards.InlineKeyboard.text(
        // biome-ignore lint/style/noNonNullAssertion: <explanation>
        this.pageInfoFormat(pagination),
        "$noop$"
      )
    ).addIf(
      pagination.hasNext,
      keyboards.InlineKeyboard.text(
        "\u27A1\uFE0F",
        this.callbackData.pack({
          type: "set",
          offset: offset + this.limitValue,
          payload: args[0]
        })
      )
    ).addIf(
      this.firstLastPage && "totalPages" in pagination && pagination.hasNext,
      keyboards.InlineKeyboard.text(
        "\u23ED\uFE0F",
        this.callbackData.pack({
          type: "set_page",
          offset: pagination.totalPages - 1,
          payload: args[0]
        })
      )
    );
    return this.wrapKeyboardHandler?.({
      keyboard,
      pagination,
      offset,
      limit: this.limitValue,
      data,
      payload: args[0]
    }) ?? keyboard;
  }
}
exports.Pagination = Pagination;