Выберите элемент в середине в ReactBootstrapTypeahead - PullRequest
0 голосов
/ 04 июля 2019

В моем компоненте ReactBootstrapTypeahead я могу щелкнуть любую опцию, чтобы выбрать ее, но использование Tab выберет только первую опцию, если она совпадает в начале строки. Если первый параметр совпадает в середине, нажатие Tab просто перемещает фокус без выбора параметра. работает , если вы впервые используете клавиши со стрелками, чтобы выделить параметр.

enter image description here

Каждая опция содержит id, make и model, и я объединяю последние два в name (например, 'Toyota Prius') для использования labelKey. Все работает, кроме выбора первого элемента без его выделения с помощью клавиш со стрелками.

<AsyncTypeahead
    id="search"
    selectHintOnEnter
    labelKey="name"
    filterBy={startsWith}
    renderMenu={renderMenu}
    options={results}
/>

Я использую пользовательскую функцию renderMenu для группировки параметров ...

const renderMenu = (results, menuProps) => {
    const items = [];
    let makesHeader, lastMake, idx = 0;
    results.forEach(result => {
        const { id, make, model } = result;
        if (!make) {
            // skip "click to load more..." text
            items.push(
                <Menu.Header key="more-header">
                    More Results Hidden…
                </Menu.Header>
            );
        }
        else if (!model) {
            if (!makesHeader) {
                items.push(
                    <Menu.Header key="makes-header">
                        Makes
                    </Menu.Header>
                );
                makesHeader = true;
            }
            items.push(
                <MenuItem key={id} option={result} position={idx}>
                    <WordHighlighter search={menuProps.text}>
                        {make}
                    </WordHighlighter>
                </MenuItem>
            );
        }
        else {
            if (make !== lastMake) {
                items.push(
                    <Menu.Header key={`${make}-header`}>
                        {make}
                    </Menu.Header>
                );
                lastMake = make;
            }
            items.push(
                <MenuItem key={id} option={result} position={idx}>
                    <WordHighlighter search={menuProps.text}>
                        {model}
                    </WordHighlighter>
                </MenuItem>
            );
        }
        idx++;
    });
    return <Menu {...menuProps}>{items}</Menu>;
};

... и выделять совпадения только в начале любого слова.

const WordHighlighter = props => {
    const search = props.search.toLowerCase(),
        len = search.length,
        parts = [];
    let count = 0;
    props.children.split(' ').forEach(word => {
        if (word.toLowerCase().startsWith(search)) {
            parts.push(<mark className="rbt-highlight-text" 
                             key={++count}>{word.substr(0, len)}</mark>);
            parts.push(<span key={++count}>{word.substr(len) + ' '}</span>);
        }
        else {
            parts.push(<span key={++count}>{word + ' '}</span>);
        }
    });
    return <span>{parts}</span>;
};

1 Ответ

0 голосов
/ 07 июля 2019

Я решил это путем переопределения обратного вызова onKeyDown Typeahead своим собственным.

const typeahead = useRef();

const onChange = useCallback(([ result ]) => {
    typeahead.current.getInstance().clear();
    typeahead.current.getInstance().blur();
    history.push(`/cars/${result.slug}`);
}, [ typeahead ]);

useEffect(() => {
    const instance = typeahead.current.getInstance(),
        original = instance._handleKeyDown;
    instance._handleKeyDown = (e, results, isMenuDown) => {
        if (results && results.length && (e.key === 'Enter' || e.key === 'Tab')) {
            e.preventDefault();
            e.stopPropagation();
            onChange(results);
        }
        else {
            return original(e, results, isMenuDown);
        }
    };
}, [ typeahead, onChange ]);
...