You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

113 lines
2.9 KiB

4 years ago
  1. 'use strict';
  2. const Select = require('./select');
  3. const highlight = (input, color) => {
  4. let val = input.toLowerCase();
  5. return str => {
  6. let s = str.toLowerCase();
  7. let i = s.indexOf(val);
  8. let colored = color(str.slice(i, i + val.length));
  9. return i >= 0 ? str.slice(0, i) + colored + str.slice(i + val.length) : str;
  10. };
  11. };
  12. class AutoComplete extends Select {
  13. constructor(options) {
  14. super(options);
  15. this.cursorShow();
  16. }
  17. moveCursor(n) {
  18. this.state.cursor += n;
  19. }
  20. dispatch(ch) {
  21. return this.append(ch);
  22. }
  23. space(ch) {
  24. return this.options.multiple ? super.space(ch) : this.append(ch);
  25. }
  26. append(ch) {
  27. let { cursor, input } = this.state;
  28. this.input = input.slice(0, cursor) + ch + input.slice(cursor);
  29. this.moveCursor(1);
  30. return this.complete();
  31. }
  32. delete() {
  33. let { cursor, input } = this.state;
  34. if (!input) return this.alert();
  35. this.input = input.slice(0, cursor - 1) + input.slice(cursor);
  36. this.moveCursor(-1);
  37. return this.complete();
  38. }
  39. deleteForward() {
  40. let { cursor, input } = this.state;
  41. if (input[cursor] === void 0) return this.alert();
  42. this.input = `${input}`.slice(0, cursor) + `${input}`.slice(cursor + 1);
  43. return this.complete();
  44. }
  45. number(ch) {
  46. return this.append(ch);
  47. }
  48. async complete() {
  49. this.completing = true;
  50. this.choices = await this.suggest(this.input, this.state._choices);
  51. this.state.limit = void 0; // allow getter/setter to reset limit
  52. this.index = Math.min(Math.max(this.visible.length - 1, 0), this.index);
  53. await this.render();
  54. this.completing = false;
  55. }
  56. suggest(input = this.input, choices = this.state._choices) {
  57. if (typeof this.options.suggest === 'function') {
  58. return this.options.suggest.call(this, input, choices);
  59. }
  60. let str = input.toLowerCase();
  61. return choices.filter(ch => ch.message.toLowerCase().includes(str));
  62. }
  63. pointer() {
  64. return '';
  65. }
  66. format() {
  67. if (!this.focused) return this.input;
  68. if (this.options.multiple && this.state.submitted) {
  69. return this.selected.map(ch => this.styles.primary(ch.message)).join(', ');
  70. }
  71. if (this.state.submitted) {
  72. let value = this.value = this.input = this.focused.value;
  73. return this.styles.primary(value);
  74. }
  75. return this.input;
  76. }
  77. async render() {
  78. if (this.state.status !== 'pending') return super.render();
  79. let style = this.options.highlight
  80. ? this.options.highlight.bind(this)
  81. : this.styles.placeholder;
  82. let color = highlight(this.input, style);
  83. let choices = this.choices;
  84. this.choices = choices.map(ch => ({ ...ch, message: color(ch.message) }));
  85. await super.render();
  86. this.choices = choices;
  87. }
  88. submit() {
  89. if (this.options.multiple) {
  90. this.value = this.selected.map(ch => ch.name);
  91. }
  92. return super.submit();
  93. }
  94. }
  95. module.exports = AutoComplete;