summaryrefslogtreecommitdiff
path: root/addons/point_of_sale/static/src/js/Misc/SearchBar.js
blob: e9f56feaf816eca93726d716018d2c359bbe81be (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
odoo.define('point_of_sale.SearchBar', function (require) {
    'use strict';

    const { useState, useExternalListener } = owl.hooks;
    const PosComponent = require('point_of_sale.PosComponent');
    const Registries = require('point_of_sale.Registries');

    /**
     * This is a simple configurable search bar component. It has search fields
     * and selection filter. Search fields allow the users to specify the type
     * of their searches. The filter is a dropdown menu for selection. Depending on
     * user's action, this component emits corresponding event with the action
     * information (payload).
     *
     * TODO: This component can be made more generic and be able to replace
     * all the search bars across pos ui.
     *
     * @prop {{
     *  config: {
     *      searchFields: string[],
     *      filter: { show: boolean, options: string[] }
     *  },
     *  placeholder: string,
     * }}
     * @emits search @payload { fieldValue: string, searchTerm: '' }
     * @emits filter-selected @payload { filter: string }
     *
     * NOTE: The payload of the emitted event is accessible via the `detail`
     * field of the event.
     */
    class SearchBar extends PosComponent {
        constructor() {
            super(...arguments);
            this.config = this.props.config;
            this.state = useState({
                searchInput: '',
                selectedFieldId: this.config.searchFields.length ? 0 : null,
                showSearchFields: false,
                showFilterOptions: false,
                selectedFilter: this.config.filter.options[0] || this.env._t('Select'),
            });
            useExternalListener(window, 'click', this._hideOptions);
        }
        selectFilter(option) {
            this.state.selectedFilter = option;
            this.trigger('filter-selected', { filter: this.state.selectedFilter });
        }
        get placeholder() {
            return this.props.placeholder;
        }
        /**
         * When vertical arrow keys are pressed, select fields for searching.
         * When enter key is pressed, trigger search event if there is searchInput.
         */
        onKeydown(event) {
            if (['ArrowUp', 'ArrowDown'].includes(event.key)) {
                event.preventDefault();
                this.state.selectedFieldId = this._fieldIdToSelect(event.key);
            } else if (event.key === 'Enter') {
                this.trigger('search', {
                    fieldValue: this.config.searchFields[this.state.selectedFieldId],
                    searchTerm: this.state.searchInput,
                });
                this.state.showSearchFields = false;
            } else {
                if (this.state.selectedFieldId === null && this.config.searchFields.length) {
                    this.state.selectedFieldId = 0;
                }
                this.state.showSearchFields = true;
            }
        }
        /**
         * Called when a search field is clicked.
         */
        onClickSearchField(id) {
            this.state.showSearchFields = false;
            this.trigger('search', {
                fieldValue: this.config.searchFields[id],
                searchTerm: this.state.searchInput,
            });
        }
        /**
         * Given an arrow key, return the next selectedFieldId.
         * E.g. If the selectedFieldId is 1 and ArrowDown is pressed, return 2.
         *
         * @param {string} key vertical arrow key
         */
        _fieldIdToSelect(key) {
            const length = this.config.searchFields.length;
            if (!length) return null;
            if (this.state.selectedFieldId === null) return 0;
            const current = this.state.selectedFieldId || length;
            return (current + (key === 'ArrowDown' ? 1 : -1)) % length;
        }
        _hideOptions() {
            this.state.showFilterOptions = false;
            this.state.showSearchFields = false;
        }
    }
    SearchBar.template = 'point_of_sale.SearchBar';
    SearchBar.defaultProps = {
        config: {
            searchFields: [],
            filter: {
                show: false,
                options: [],
            },
        },
        placeholder: 'Search ...',
    };

    Registries.Component.add(SearchBar);

    return SearchBar;
});