<commit_before>import { dispatch as d3_dispatch } from 'd3-dispatch';

import {
    event as d3_event,
    select as d3_select,
    selectAll as d3_selectAll
} from 'd3-selection';

import {
    modeAddArea,
    modeAddLine,
    modeAddPoint
} from '../modes';

import { t, textDirection } from '../util/locale';
import { actionChangePreset } from '../actions/index';
import { operationDelete } from '../operations/index';
import { svgIcon } from '../svg/index';
import { tooltip } from '../util/tooltip';
import { uiPresetFavorite } from './preset_favorite';
import { uiPresetIcon } from './preset_icon';
import { uiTagReference } from './tag_reference';
import { utilKeybinding, utilNoAuto, utilRebind } from '../util';


export function uiSearchAdd(context) {
    var dispatch = d3_dispatch('choose');
    var presets;
    var search = d3_select(null), popover = d3_select(null), list = d3_select(null);

    function searchAdd(selection) {

        presets = context.presets().matchAnyGeometry(['point', 'line', 'area']);

        var searchWrap = selection
            .append('div')
            .attr('class', 'search-wrap');

        search = searchWrap
            .append('input')
            .attr('class', 'search-input')
            .attr('placeholder', t('modes.add_feature.title'))
            .attr('type', 'search')
            .call(utilNoAuto)
            .on('keypress', function() {
                // enter/return
                if (d3_event.keyCode === 13) {
                    popover.selectAll('.list .list-item.focused button.choose')
                        .each(function(d) { d.choose.call(this); });
                    d3_event.preventDefault();
                    d3_event.stopPropagation();
                }
            })
            .on('keydown', function(){
                // up/down arrow key navigation

                if (d3_event.keyCode === utilKeybinding.keyCodes['↓']) {
                    d3_event.preventDefault();
                    d3_event.stopPropagation();

                    var nextFocus,
                        priorFocus = popover.selectAll('.list .list-item.focused');
                    if (priorFocus.empty()) {
                        nextFocus = popover.selectAll('.list > .list-item:first-child');
                    } else {
                        nextFocus = popover.selectAll('.list .list-item.focused + .list-item');
                        if (nextFocus.empty()) {
                            nextFocus = d3_select(priorFocus.nodes()[0].nextElementSibling)
                                .selectAll('.list-item:first-child');
                        }
                    }
                    if (!nextFocus.empty()) {
                        focusListItem(nextFocus);
                        priorFocus.classed('focused', false);
                    }

                } else if (d3_event.keyCode === utilKeybinding.keyCodes['↑']) {
                    d3_event.preventDefault();
                    d3_event.stopPropagation();

                    var priorFocus = popover.selectAll('.list .list-item.focused');
                    if (!priorFocus.empty()) {
                        var nextFocus = d3_select(priorFocus.nodes()[0].previousElementSibling);
                        if (!nextFocus.empty() && !nextFocus.classed('list-item')) {
                            nextFocus = nextFocus.selectAll('.list-item:last-child');
                        }
                        if (!nextFocus.empty()) {
                            focusListItem(nextFocus);
                            priorFocus.classed('focused', false);
                        }
                    }
                }
            })
            .on('mousedown', function() {
                search.attr('clicking', true);
            })
            .on('mouseup', function() {
                search.attr('clicking', null);
            })
            .on('focus', function() {
                if (search.attr('clicking')) {
                    search.attr('focusing', true);
                    search.attr('clicking', null);
                } else {
                    search.node().setSelectionRange(0, search.property('value').length);
                }
                popover.classed('hide', false);
            })
            .on('blur', function() {
                popover.classed('hide', true);
            })
            .on('click', function() {
                if (search.attr('focusing')) {
                    search.node().setSelectionRange(0, search.property('value').length);
                    search.attr('focusing', null);
                }
            })
            .on('input', function () {
                var value = search.property('value');
                //list.classed('filtered', value.length);
                if (value.length) {
                    popover.selectAll('.subsection').remove();
                    var results = presets.search(value);
                    list.call(drawList, results);
                    popover.selectAll('.list .list-item.focused')
                        .classed('focused', false);
                    focusListItem(popover.selectAll('.list > .list-item:first-child'));
                } else {
                    //list.call(drawList, context.presets().defaults(geometry, 36));
                }
            });

        searchWrap
            .call(svgIcon('#iD-icon-search', 'search-icon pre-text'));

        popover = selection
            .append('div')
            .attr('class', 'popover fillL hide')
            .on('mousedown', function() {
                // don't blur the search input (and thus close results)
                d3_event.preventDefault();
                d3_event.stopPropagation();
            });

        list = popover
            .append('div')
            .attr('class', 'list');//
            //.call(drawList, context.presets().defaults(geometry, 36));

        context.features().on('change.search-add', updateForFeatureHiddenState);

        context.keybinding().on('1', function() {
            search.node().focus();
            d3_event.preventDefault();
            d3_event.stopPropagation();
        });
    }

    function focusListItem(selection) {
        if (!selection.empty()) {
            selection.classed('focused', true);
            //selection.nodes()[0].scrollIntoView();
        }
    }

    function drawList(list, presets) {

        var collection = presets.collection.map(function(preset) {
            if (preset.members) {
                return CategoryItem(preset);
            } else if (preset.visible()) {
                var supportedGeometry = preset.geometry.filter(function(geometry) {
                    return ['point', 'line', 'area'].indexOf(geometry) !== -1;
                }).sort();
                if (supportedGeometry.length === 1) {
                    return AddablePresetItem(preset, supportedGeometry[0]);
                }
                return MultiGeometryPresetItem(preset, supportedGeometry);
            }
        });
<commit_msg>Keep the focused item visible when navigating the search-to-add results with the arrow keys<commit_after>import { dispatch as d3_dispatch } from 'd3-dispatch';

import {
    event as d3_event,
    select as d3_select,
    selectAll as d3_selectAll
} from 'd3-selection';

import {
    modeAddArea,
    modeAddLine,
    modeAddPoint
} from '../modes';

import { t, textDirection } from '../util/locale';
import { actionChangePreset } from '../actions/index';
import { operationDelete } from '../operations/index';
import { svgIcon } from '../svg/index';
import { tooltip } from '../util/tooltip';
import { uiPresetFavorite } from './preset_favorite';
import { uiPresetIcon } from './preset_icon';
import { uiTagReference } from './tag_reference';
import { utilKeybinding, utilNoAuto, utilRebind } from '../util';


export function uiSearchAdd(context) {
    var dispatch = d3_dispatch('choose');
    var presets;
    var search = d3_select(null), popover = d3_select(null), list = d3_select(null);

    function searchAdd(selection) {

        presets = context.presets().matchAnyGeometry(['point', 'line', 'area']);

        var searchWrap = selection
            .append('div')
            .attr('class', 'search-wrap');

        search = searchWrap
            .append('input')
            .attr('class', 'search-input')
            .attr('placeholder', t('modes.add_feature.title'))
            .attr('type', 'search')
            .call(utilNoAuto)
            .on('keypress', function() {
                // enter/return
                if (d3_event.keyCode === 13) {
                    popover.selectAll('.list .list-item.focused button.choose')
                        .each(function(d) { d.choose.call(this); });
                    d3_event.preventDefault();
                    d3_event.stopPropagation();
                }
            })
            .on('keydown', function(){
                // up/down arrow key navigation

                if (d3_event.keyCode === utilKeybinding.keyCodes['↓']) {
                    d3_event.preventDefault();
                    d3_event.stopPropagation();

                    var nextFocus,
                        priorFocus = popover.selectAll('.list .list-item.focused');
                    if (priorFocus.empty()) {
                        nextFocus = popover.selectAll('.list > .list-item:first-child');
                    } else {
                        nextFocus = popover.selectAll('.list .list-item.focused + .list-item');
                        if (nextFocus.empty()) {
                            nextFocus = d3_select(priorFocus.nodes()[0].nextElementSibling)
                                .selectAll('.list-item:first-child');
                        }
                    }
                    if (!nextFocus.empty()) {
                        focusListItem(nextFocus);
                        priorFocus.classed('focused', false);
                    }

                } else if (d3_event.keyCode === utilKeybinding.keyCodes['↑']) {
                    d3_event.preventDefault();
                    d3_event.stopPropagation();

                    var priorFocus = popover.selectAll('.list .list-item.focused');
                    if (!priorFocus.empty()) {
                        var nextFocus = d3_select(priorFocus.nodes()[0].previousElementSibling);
                        if (!nextFocus.empty() && !nextFocus.classed('list-item')) {
                            nextFocus = nextFocus.selectAll('.list-item:last-child');
                        }
                        if (!nextFocus.empty()) {
                            focusListItem(nextFocus);
                            priorFocus.classed('focused', false);
                        }
                    }
                }
            })
            .on('mousedown', function() {
                search.attr('clicking', true);
            })
            .on('mouseup', function() {
                search.attr('clicking', null);
            })
            .on('focus', function() {
                if (search.attr('clicking')) {
                    search.attr('focusing', true);
                    search.attr('clicking', null);
                } else {
                    search.node().setSelectionRange(0, search.property('value').length);
                }
                popover.classed('hide', false);
            })
            .on('blur', function() {
                popover.classed('hide', true);
            })
            .on('click', function() {
                if (search.attr('focusing')) {
                    search.node().setSelectionRange(0, search.property('value').length);
                    search.attr('focusing', null);
                }
            })
            .on('input', function () {
                var value = search.property('value');
                //list.classed('filtered', value.length);
                if (value.length) {
                    popover.selectAll('.subsection').remove();
                    var results = presets.search(value);
                    list.call(drawList, results);
                    popover.selectAll('.list .list-item.focused')
                        .classed('focused', false);
                    focusListItem(popover.selectAll('.list > .list-item:first-child'));
                } else {
                    //list.call(drawList, context.presets().defaults(geometry, 36));
                }
            });

        searchWrap
            .call(svgIcon('#iD-icon-search', 'search-icon pre-text'));

        popover = selection
            .append('div')
            .attr('class', 'popover fillL hide')
            .on('mousedown', function() {
                // don't blur the search input (and thus close results)
                d3_event.preventDefault();
                d3_event.stopPropagation();
            });

        list = popover
            .append('div')
            .attr('class', 'list');//
            //.call(drawList, context.presets().defaults(geometry, 36));

        context.features().on('change.search-add', updateForFeatureHiddenState);

        context.keybinding().on('1', function() {
            search.node().focus();
            d3_event.preventDefault();
            d3_event.stopPropagation();
        });
    }

    function focusListItem(selection) {
        if (!selection.empty()) {
            selection.classed('focused', true);
            var node = selection.nodes()[0];
            var popoverNode = popover.node();
            var nodeRect = node.getBoundingClientRect();

            // scroll to keep the focused item visible
            if (node.offsetTop < popoverNode.scrollTop) {
                popoverNode.scrollTop = node.offsetTop;

            } else if (node.offsetTop + node.offsetHeight > popoverNode.scrollTop + popoverNode.offsetHeight) {
                popoverNode.scrollTop = node.offsetTop + node.offsetHeight - popoverNode.offsetHeight;
            }
        }
    }

    function drawList(list, presets) {

        var collection = presets.collection.map(function(preset) {
            if (preset.members) {
                return CategoryItem(preset);
            } else if (preset.visible()) {
                var supportedGeometry = preset.geometry.filter(function(geometry) {
                    return ['point', 'line', 'area'].indexOf(geometry) !== -1;
                }).sort();
                if (supportedGeometry.length === 1) {
                    return AddablePresetItem(preset, supportedGeometry[0]);
                }
                return MultiGeometryPresetItem(preset, supportedGeometry);
            }
        });

{'fraction_non_alphanumeric': 0.13326974177284187, 'fraction_numerical': 0.0054507051849833075, 'mean_word_length': 1.504778156996587, 'pattern_counts': {'":': 0, '<': 4, '<?xml version=': 0, '>': 8, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7286276', 'n_tokens_mistral': 3836, 'n_tokens_neox': 3622, 'n_words': 661}