Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature]: inquirer/search - external pagination support (or be able to pass search value back to new instance) #1489

Open
tanepiper opened this issue Jul 30, 2024 · 3 comments

Comments

@tanepiper
Copy link

Description:

I'm using search to implement a cli tool that allows you to pass a name, which is then queried against a product database. The API has a max request of 50 items, but will have more than 50 results so a page value can be passed.

It would be good to either have a pagination option that can allow the search to recall the method with new parameters but keep the search query.

The other option I can see is if I provide my own value here to handle as an answer I can call the search function again, but to do this I would want to be able to pass in the original value to display.

@tanepiper tanepiper changed the title [Feature]: inquirer/search - pagination support (or be able to pass value back to new instance) Jul 30, 2024
@tanepiper tanepiper changed the title [Feature]: inquirer/search - pagination support (or be able to pass search value back to new instance) Jul 30, 2024
@SBoudrias
Copy link
Owner

Hi, this topic was brought up here #1487 too. I'm currently leaning on leaving this up to custom prompts - but I'll consider any concrete proposal.

What would you like the API to look like? Some pseudo code of what the API could look like would be useful.

@paul-uz
Copy link

paul-uz commented Jul 30, 2024

I'm going to have a go at this. Config for the prompt could include setting your pagination query params etc, I think. My use case is paginating calls to AWS services, not REST APIs, but I reckon they'd follow a similar approach?

@tanepiper
Copy link
Author

tanepiper commented Jul 30, 2024

Here's how I ended up re-writing my search app to work - as you can see when I go to previous or next pages I have to change the message (I also clear the screen and increase the size but that's a preference)

public async search(searchTerm?: string, page = 0) {
    let message = 'Search for an Product Name';
    if (searchTerm) {
      message = `Search for an Product Name (Current: ${searchTerm})`;
    }

    const answer = await search<string>({
      pageSize: page === 0 ? 5 : 10,
      message,
      theme: {
        helpMode: 'always',
        prefix: '🔍',
      },
      source: async (input: string, { signal }: { signal: AbortSignal }) => {
       await setTimeout(1000);

        if (signal?.aborted) return [];

        if (!searchTerm && !input) {
          return [];
        }

        // Return a promise that resolves after the debounce period
        try {
          const products = await this.client.itemsSearch(
            input ?? searchTerm,
            page,
            50,
            signal,
          );
          const results =
            page > 0
              ? [
                  {
                    name: 'Previous Results',
                    value: `-${input ?? searchTerm}`,
                    description: 'Go to previous results page',
                  },
                ]
              : [];

          products?.forEach(({ content }, index) =>
            results.push({
              name: item.name,
              value: item.id,
              description: `Page: ${page + 1} | Record ${index + 1} / ${products.length} | ${content.id} `,
            }),
          );
          if (products?.length === 50) {
            results.push({
              name: 'Next Results',
              value: `+${input ?? searchTerm}`,
              description: 'Select to load the next page',
            });
          }
          return results;
        } catch (error) {
            console.error('\n Error during search:', error.message);
            return [];
          }
        }
      },
    }).then((answer) => {
      if (!isCorrectID(answer.split('-')?.[1])) {
        page = answer[0] === '-' ? page - 1 : page + 1;
        process.stdout.write('\x1Bc');
        return this.search(answer.substring(1), page);
      }
      return answer;
    });
    return answer;
  }

Looking at the design I guess it would be best to provide it along with the answer. Maybe have it as options on the setup (to enable prev/next) and then in the response:

.then((answer, { prev, next }) => {
  if (prev) {
     return ....
  }
})

Yes the user has to handle it, but you provide a hook into it.

The only other way I see it to make it event-based

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
3 participants