import { ForceReply, InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove, } from './core/types/typegram' import { is2D } from './core/helpers/check' type Hideable = B & { hide?: boolean } type HideableKBtn = Hideable type HideableIKBtn = Hideable export class Markup< T extends | InlineKeyboardMarkup | ReplyKeyboardMarkup | ReplyKeyboardRemove | ForceReply > { constructor(readonly reply_markup: T) {} selective( this: Markup, value = true ) { return new Markup({ ...this.reply_markup, selective: value }) } placeholder( this: Markup, placeholder: string ) { return new Markup({ ...this.reply_markup, input_field_placeholder: placeholder, }) } resize(this: Markup, value = true) { return new Markup({ ...this.reply_markup, resize_keyboard: value, }) } oneTime(this: Markup, value = true) { return new Markup({ ...this.reply_markup, one_time_keyboard: value, }) } } export * as button from './button' export function removeKeyboard(): Markup { return new Markup({ remove_keyboard: true }) } export function forceReply(): Markup { return new Markup({ force_reply: true }) } export function keyboard(buttons: HideableKBtn[][]): Markup export function keyboard( buttons: HideableKBtn[], options?: Partial> ): Markup export function keyboard( buttons: HideableKBtn[] | HideableKBtn[][], options?: Partial> ): Markup { const keyboard = buildKeyboard(buttons, { columns: 1, ...options, }) return new Markup({ keyboard }) } export function inlineKeyboard( buttons: HideableIKBtn[][] ): Markup export function inlineKeyboard( buttons: HideableIKBtn[], options?: Partial> ): Markup export function inlineKeyboard( buttons: HideableIKBtn[] | HideableIKBtn[][], options?: Partial> ): Markup { const inlineKeyboard = buildKeyboard(buttons, { columns: buttons.length, ...options, }) return new Markup({ inline_keyboard: inlineKeyboard }) } interface KeyboardBuildingOptions { wrap?: (btn: B, index: number, currentRow: B[]) => boolean columns: number } function buildKeyboard( buttons: B[] | B[][], options: KeyboardBuildingOptions ): B[][] { const result: B[][] = [] if (!Array.isArray(buttons)) { return result } if (is2D(buttons)) { return buttons.map((row) => row.filter((button) => !button.hide)) } const wrapFn = options.wrap !== undefined ? options.wrap : (_btn: B, _index: number, currentRow: B[]) => currentRow.length >= options.columns let currentRow: B[] = [] let index = 0 for (const btn of buttons.filter((button) => !button.hide)) { if (wrapFn(btn, index, currentRow) && currentRow.length > 0) { result.push(currentRow) currentRow = [] } currentRow.push(btn) index++ } if (currentRow.length > 0) { result.push(currentRow) } return result }