paginator.js 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. import chalk from 'chalk';
  2. /**
  3. * The paginator returns a subset of the choices if the list is too long.
  4. */
  5. export default class Paginator {
  6. /**
  7. * @param {import("./screen-manager")} [screen]
  8. * @param {{isInfinite?: boolean}} [options]
  9. */
  10. constructor(screen, options = {}) {
  11. const { isInfinite = true } = options;
  12. this.lastIndex = 0;
  13. this.screen = screen;
  14. this.isInfinite = isInfinite;
  15. }
  16. paginate(output, active, pageSize) {
  17. pageSize = pageSize || 7;
  18. let lines = output.split('\n');
  19. if (this.screen) {
  20. lines = this.screen.breakLines(lines);
  21. active = lines
  22. .map((lineParts) => lineParts.length)
  23. .splice(0, active)
  24. .reduce((a, b) => a + b, 0);
  25. lines = lines.flat();
  26. }
  27. // Make sure there's enough lines to paginate
  28. if (lines.length <= pageSize) {
  29. return output;
  30. }
  31. const visibleLines = this.isInfinite
  32. ? this.getInfiniteLines(lines, active, pageSize)
  33. : this.getFiniteLines(lines, active, pageSize);
  34. this.lastIndex = active;
  35. return (
  36. visibleLines.join('\n') +
  37. '\n' +
  38. chalk.dim('(Move up and down to reveal more choices)')
  39. );
  40. }
  41. getInfiniteLines(lines, active, pageSize) {
  42. if (this.pointer === undefined) {
  43. this.pointer = 0;
  44. }
  45. const middleOfList = Math.floor(pageSize / 2);
  46. // Move the pointer only when the user go down and limit it to the middle of the list
  47. if (
  48. this.pointer < middleOfList &&
  49. this.lastIndex < active &&
  50. active - this.lastIndex < pageSize
  51. ) {
  52. this.pointer = Math.min(middleOfList, this.pointer + active - this.lastIndex);
  53. }
  54. // Duplicate the lines so it give an infinite list look
  55. const infinite = [lines, lines, lines].flat();
  56. const topIndex = Math.max(0, active + lines.length - this.pointer);
  57. return infinite.splice(topIndex, pageSize);
  58. }
  59. getFiniteLines(lines, active, pageSize) {
  60. let topIndex = active - pageSize / 2;
  61. if (topIndex < 0) {
  62. topIndex = 0;
  63. } else if (topIndex + pageSize > lines.length) {
  64. topIndex = lines.length - pageSize;
  65. }
  66. return lines.splice(topIndex, pageSize);
  67. }
  68. }