@shopify/cli-kit
Version:
A set of utilities, interfaces, and models that are common across all the platform features
89 lines • 4.8 kB
JavaScript
import { SelectInput } from './SelectInput.js';
import { TextInput } from './TextInput.js';
import { PromptLayout } from './Prompts/PromptLayout.js';
import { throttle } from '../../../../public/common/function.js';
import usePrompt, { PromptState } from '../hooks/use-prompt.js';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { Box, useApp } from 'ink';
const MIN_NUMBER_OF_ITEMS_FOR_SEARCH = 5;
// eslint-disable-next-line react/function-component-definition
function AutocompletePrompt({ message, choices, infoTable, onSubmit, search, hasMorePages: initialHasMorePages = false, abortSignal, infoMessage, groupOrder, }) {
const { exit: unmountInk } = useApp();
const [searchTerm, setSearchTerm] = useState('');
const [searchResults, setSearchResults] = useState(choices);
const canSearch = choices.length > MIN_NUMBER_OF_ITEMS_FOR_SEARCH;
const [hasMorePages, setHasMorePages] = useState(initialHasMorePages);
const { promptState, setPromptState, answer, setAnswer } = usePrompt({
initialAnswer: undefined,
});
const paginatedSearch = useCallback(async (term) => {
const results = await search(term);
return results;
}, [search]);
const submitAnswer = useCallback((answer) => {
if (promptState === PromptState.Idle) {
setAnswer(answer);
setPromptState(PromptState.Submitted);
}
}, [promptState, setAnswer, setPromptState]);
useEffect(() => {
if (promptState === PromptState.Submitted && answer) {
setSearchTerm('');
unmountInk();
onSubmit(answer.value);
}
}, [answer, onSubmit, promptState, unmountInk]);
const setLoadingWhenSlow = useRef();
// we want to set it each time so that searchTermRef always tracks searchTerm,
// this is NOT the same as writing useRef(searchTerm)
const searchTermRef = useRef('');
searchTermRef.current = searchTerm;
// Keep current values in refs to avoid stale closures
const choicesRef = useRef(choices);
choicesRef.current = choices;
const initialHasPagesRef = useRef(initialHasMorePages);
initialHasPagesRef.current = initialHasMorePages;
// useMemo ensures debounceSearch is not recreated on every render
const debounceSearch = React.useMemo(() => throttle((term) => {
setLoadingWhenSlow.current = setTimeout(() => {
setPromptState(PromptState.Loading);
}, 100);
paginatedSearch(term)
.then((result) => {
// while we were waiting for the promise to resolve, the user
// has emptied the search term, so we want to show the default
// choices instead
if (searchTermRef.current.length === 0) {
setSearchResults(choicesRef.current);
setHasMorePages(initialHasPagesRef.current);
}
else {
setSearchResults(result.data);
setHasMorePages(result.meta?.hasNextPage ?? false);
}
setPromptState(PromptState.Idle);
})
.catch(() => {
setPromptState(PromptState.Error);
})
.finally(() => {
clearTimeout(setLoadingWhenSlow.current);
});
}, 400, { leading: true, trailing: true }), [paginatedSearch, setPromptState]);
return (React.createElement(PromptLayout, { message: message, state: promptState, infoTable: infoTable, infoMessage: infoMessage, abortSignal: abortSignal, header: promptState !== PromptState.Submitted && canSearch ? (React.createElement(Box, { marginLeft: 3 },
React.createElement(TextInput, { value: searchTerm, onChange: (term) => {
setSearchTerm(term);
if (term.length > 0) {
debounceSearch(term);
}
else {
debounceSearch.cancel();
setPromptState(PromptState.Idle);
setSearchResults(choices);
}
}, placeholder: "Type to search..." }))) : null, submittedAnswerLabel: answer?.label, input: React.createElement(SelectInput, { items: searchResults, initialItems: choices, enableShortcuts: false, emptyMessage: "No results found.", highlightedTerm: searchTerm, loading: promptState === PromptState.Loading, errorMessage: promptState === PromptState.Error
? 'There has been an error while searching. Please try again later.'
: undefined, hasMorePages: hasMorePages, morePagesMessage: "Find what you're looking for by typing its name.", onSubmit: submitAnswer, groupOrder: groupOrder }) }));
}
export { AutocompletePrompt };
//# sourceMappingURL=AutocompletePrompt.js.map