UNPKG

@shopify/cli-kit

Version:

A set of utilities, interfaces, and models that are common across all the platform features

89 lines 4.8 kB
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