import React, {MutableRefObject, useCallback, useMemo, useRef, useState} from "react"; import {classes} from "../../Utils"; import {Check} from "@phosphor-icons/react"; /** * Suggestions preselected options navigation configuration. */ export interface SuggestionsNavigation { /** * Return true if the preselected options navigation is initialized. */ initialized(): boolean; /** * Preselect the next option in the suggestions. */ next(): void; /** * Preselect the previous option in the suggestions. */ previous(): void; /** * Select the currently preselected option. */ select(): void; } /** * Hook to get the preselected options navigation reference. */ export function useSuggestionsNavigation(): MutableRefObject { return useRef({ initialized(): boolean { return false; }, next(): void { }, previous(): void { }, select(): void { }, } as SuggestionsNavigation); } export function OptionsSuggestions({options, onSelected, selectedOptions, renderOption, navigator}: { /** * Options to suggest. */ options: Record; /** * Called when an option is selected. * @param key * @param option */ onSelected: (key: OptionKey, option: Option) => void; /** * Already selected options that will be shown differently. */ selectedOptions?: Record; /** * Render an option. * @param option The option to render. */ renderOption?: (option: Option) => React.ReactNode; /** * A reference to a preselected suggestions options navigation object. */ navigator?: MutableRefObject; }): React.ReactNode { // Initialize default option render function. const defaultRenderOption = useCallback((option: Option) => (String(option)), []); const optionsArray = useMemo(() => (Object.entries(options) as [OptionKey, Option][]), [options]); const [preselectedOptionIndex, setPreselectedOptionIndex] = useState(0); const navigation = useMemo(() => ({ initialized(): boolean { return true; }, next(): void { // Preselect the next option in the options array, or the first one if it's the last element. setPreselectedOptionIndex((optionsArray.length == (preselectedOptionIndex + 1)) ? 0 : (preselectedOptionIndex + 1)); }, previous(): void { // Preselect the previous option in the options array, or the last one if it's the first element. setPreselectedOptionIndex(((preselectedOptionIndex - 1) < 0) ? (optionsArray.length - 1) : (preselectedOptionIndex - 1)); }, select(): void { // Get the currently preselected option. const [optionKey, option] = optionsArray[preselectedOptionIndex]; // Select the currently preselected option. onSelected(optionKey, option); }, }), [optionsArray, preselectedOptionIndex, setPreselectedOptionIndex]); if (navigator) // If navigator reference is set, assigning it. navigator.current = navigation; return optionsArray.map(([key, option], index) => ( { onSelected(key, option); }}> {(renderOption ?? defaultRenderOption)(option)} )); }