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;
});
|