paginator.js 2.2 KB

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