Skip to content

Commit

Permalink
Autocomplete Pagination (#136)
Browse files Browse the repository at this point in the history
  • Loading branch information
elie-g authored and terkelg committed Mar 2, 2019
1 parent 4aa9d07 commit d823758
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 23 deletions.
4 changes: 4 additions & 0 deletions example.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,16 @@ let interval;
name: 'actor',
message: 'Pick your favorite actor',
initial: 1,
limit: 3,
choices: [
{ title: 'Cage' },
{ title: 'Clooney', value: 'silver-fox' },
{ title: 'Gyllenhaal' },
{ title: 'Gibson' },
{ title: 'Grant' },
{ title: 'Smith' },
{ title: 'Hanks' },
{ title: 'Downey Jr.' }
]
},
{
Expand Down
70 changes: 48 additions & 22 deletions lib/elements/autocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ class AutocompletePrompt extends Prompt {
`${figures.pointerSmall} ${getTitle(this.choices, this.initial)}` :
`${figures.pointerSmall} ${opts.noMatches || 'no matches found'}`
);
this.suggestions = [];
this.suggestions = [[]];
this.page = 0;
this.input = '';
this.limit = opts.limit || 10;
this.cursor = 0;
Expand All @@ -57,13 +58,13 @@ class AutocompletePrompt extends Prompt {

moveSelect(i) {
this.select = i;
if (this.suggestions.length > 0) {
this.value = getVal(this.suggestions, i);
if (this.suggestions[this.page].length > 0) {
this.value = getVal(this.suggestions[this.page], i);
} else {
this.value = this.initial !== undefined
? getVal(this.choices, this.initial)
: null;
};
}
this.fire();
}

Expand All @@ -72,21 +73,24 @@ class AutocompletePrompt extends Prompt {
const suggestions = await p;

if (this.completing !== p) return;
this.suggestions = suggestions.slice(0, this.limit)
.map((s, i, arr) => ({title: getTitle(arr, i), value: getVal(arr, i)}));
this.suggestions = suggestions
.map((s, i, arr) => ({title: getTitle(arr, i), value: getVal(arr, i)}))
.reduce((arr, sug) => {
if (arr[arr.length - 1].length < this.limit)
arr[arr.length - 1].push(sug);
else arr.push([sug]);
return arr;
}, [[]]);
this.isFallback = false;
this.completing = false;
if (!this.suggestions[this.page])
this.page = 0;

if (!this.suggestions.length && this.fallback) {
const index = getIndex(this.choices, this.fallback);
this.suggestions = index !== undefined
? [
{
title: getTitle(this.choices, index),
value: getVal(this.choices, index),
},
]
: [];
this.suggestions = [[]];
if (index !== undefined)
this.suggestions[0].push({ title: getTitle(this.choices, index), value: getVal(this.choices, index) });
this.isFallback = true;
}

Expand Down Expand Up @@ -122,7 +126,7 @@ class AutocompletePrompt extends Prompt {
this.close();
}

_(c, key) {
_(c, key) { // TODO on ctrl+# go to page #
let s1 = this.input.slice(0, this.cursor);
let s2 = this.input.slice(this.cursor);
this.input = `${s1}${c}${s2}`;
Expand Down Expand Up @@ -156,7 +160,7 @@ class AutocompletePrompt extends Prompt {
}

last() {
this.moveSelect(this.suggestions.length - 1);
this.moveSelect(this.suggestions[this.page].length - 1);
this.render();
}

Expand All @@ -167,13 +171,32 @@ class AutocompletePrompt extends Prompt {
}

down() {
if (this.select >= this.suggestions.length - 1) return this.bell();
if (this.select >= this.suggestions[this.page].length - 1) return this.bell();
this.moveSelect(this.select + 1);
this.render();
}

next() {
this.moveSelect((this.select + 1) % this.suggestions.length);
if (this.select === this.suggestions[this.page].length - 1) {
this.page = (this.page + 1) % this.suggestions.length;
this.moveSelect(0);
} else this.moveSelect(this.select + 1);
this.render();
}

nextPage() {
if (this.page >= this.suggestions.length - 1)
return this.bell();
this.page++;
this.moveSelect(this.select);
this.render();
}

prevPage() {
if (this.page <= 0)
return this.bell();
this.page--;
this.moveSelect(this.select);
this.render();
}

Expand All @@ -197,20 +220,23 @@ class AutocompletePrompt extends Prompt {
let prompt = `${style.symbol(this.done, this.aborted)} ${this.msg} ${style.delimiter(this.completing)} `;
let length = strip(prompt).length;

if (this.done && this.suggestions[this.select]) {
prompt += `${this.suggestions[this.select].title}`;
if (this.done && this.suggestions[this.page][this.select]) {
prompt += `${this.suggestions[this.page][this.select].title}`;
} else {
this.rendered = `${this.transform.render(this.input)}`;
length += this.rendered.length;
prompt += this.rendered;
}

if (!this.done) {
this.lineCount = this.suggestions.length;
let suggestions = this.suggestions.reduce((acc, item, i) =>
this.lineCount = this.suggestions[this.page].length;
let suggestions = this.suggestions[this.page].reduce((acc, item, i) =>
acc + `\n${i === this.select ? color.cyan(item.title) : item.title}`, '');
if (suggestions && !this.isFallback) {
prompt += suggestions;
if (this.suggestions.length > 1) {
prompt += color.blue(`\nPage ${this.page+1}/${this.suggestions.length}`);
}
} else {
const fallbackIndex = getIndex(this.choices, this.fallback);
const fallbackTitle = fallbackIndex !== undefined
Expand Down
3 changes: 3 additions & 0 deletions lib/util/action.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@ module.exports = key => {
if (key.name === 'e') return 'last';
if (key.name === 'g') return 'reset';
}

if (key.name === 'return') return 'submit';
if (key.name === 'enter') return 'submit'; // ctrl + J
if (key.name === 'backspace') return 'delete';
if (key.name === 'delete') return 'deleteForward';
if (key.name === 'abort') return 'abort';
if (key.name === 'escape') return 'abort';
if (key.name === 'tab') return 'next';
if (key.name === 'pagedown') return 'nextPage';
if (key.name === 'pageup') return 'prevPage';

if (key.name === 'up') return 'up';
if (key.name === 'down') return 'down';
Expand Down
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -692,7 +692,7 @@ If you want to predefine selected values, give the choice object an `selected` p
> Interactive auto complete prompt.
The prompt will list options based on user input. Type to filter the list.
Use <kbd>up</kbd>/<kbd>down</kbd> to navigate. Use <kbd>tab</kbd> to cycle the result. Hit <kbd>enter</kbd> to select the highlighted item below the prompt.
Use <kbd>up</kbd>/<kbd>down</kbd> to navigate. Use <kbd>tab</kbd> to cycle the result. Use <kbd>Page Up</kbd>/<kbd>Page Down</kbd> to change page. Hit <kbd>enter</kbd> to select the highlighted item below the prompt.

The default suggests function is sorting based on the `title` property of the choices.
You can overwrite how choices are being filtered by passing your own suggest function.
Expand Down

0 comments on commit d823758

Please sign in to comment.