github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/ui/app/mixins/searchable.js (about) 1 import Mixin from '@ember/object/mixin'; 2 import { get, computed } from '@ember/object'; 3 import { reads } from '@ember/object/computed'; 4 import Fuse from 'fuse.js'; 5 6 /** 7 Searchable mixin 8 9 Simple search filtering behavior for a list of objects. 10 11 Properties to override: 12 - searchTerm: the string to use as a query 13 - searchProps: the props on each object to search 14 -- exactMatchSearchProps: the props for exact search when props are different per search type 15 -- regexSearchProps: the props for regex search when props are different per search type 16 -- fuzzySearchProps: the props for fuzzy search when props are different per search type 17 - exactMatchEnabled: (true) disable to not use the exact match search type 18 - fuzzySearchEnabled: (false) enable to use the fuzzy search type 19 - regexEnabled: (true) disable to disable the regex search type 20 - listToSearch: the list of objects to search 21 22 Properties provided: 23 - listSearched: a subset of listToSearch of items that meet the search criteria 24 */ 25 export default Mixin.create({ 26 searchTerm: '', 27 listToSearch: computed(() => []), 28 29 searchProps: null, 30 exactMatchSearchProps: reads('searchProps'), 31 regexSearchProps: reads('searchProps'), 32 fuzzySearchProps: reads('searchProps'), 33 34 // Three search modes 35 exactMatchEnabled: true, 36 fuzzySearchEnabled: false, 37 regexEnabled: true, 38 39 // Search should reset pagination. Not every instance of 40 // search will be paired with pagination, but it's still 41 // preferable to generalize this rather than risking it being 42 // forgotten on a single page. 43 resetPagination() { 44 if (this.currentPage != null) { 45 this.set('currentPage', 1); 46 } 47 }, 48 49 fuse: computed('listToSearch.[]', 'fuzzySearchProps.[]', function() { 50 return new Fuse(this.listToSearch, { 51 shouldSort: true, 52 threshold: 0.4, 53 location: 0, 54 distance: 100, 55 tokenize: true, 56 matchAllTokens: true, 57 maxPatternLength: 32, 58 minMatchCharLength: 1, 59 keys: this.fuzzySearchProps || [], 60 getFn(item, key) { 61 return get(item, key); 62 }, 63 }); 64 }), 65 66 listSearched: computed( 67 'searchTerm', 68 'listToSearch.[]', 69 'exactMatchEnabled', 70 'fuzzySearchEnabled', 71 'regexEnabled', 72 'exactMatchSearchProps.[]', 73 'fuzzySearchProps.[]', 74 'regexSearchProps.[]', 75 function() { 76 const searchTerm = this.searchTerm.trim(); 77 78 if (!searchTerm || !searchTerm.length) { 79 return this.listToSearch; 80 } 81 82 const results = []; 83 84 if (this.exactMatchEnabled) { 85 results.push( 86 ...exactMatchSearch( 87 searchTerm, 88 this.listToSearch, 89 this.exactMatchSearchProps 90 ) 91 ); 92 } 93 94 if (this.fuzzySearchEnabled) { 95 results.push(...this.fuse.search(searchTerm)); 96 } 97 98 if (this.regexEnabled) { 99 results.push( 100 ...regexSearch(searchTerm, this.listToSearch, this.regexSearchProps) 101 ); 102 } 103 104 return results.uniq(); 105 } 106 ), 107 }); 108 109 function exactMatchSearch(term, list, keys) { 110 if (term.length) { 111 return list.filter(item => keys.some(key => get(item, key) === term)); 112 } 113 } 114 115 function regexSearch(term, list, keys) { 116 if (term.length) { 117 try { 118 const regex = new RegExp(term, 'i'); 119 // Test the value of each key for each object against the regex 120 // All that match are returned. 121 return list.filter(item => keys.some(key => regex.test(get(item, key)))); 122 } catch (e) { 123 // Swallow the error; most likely due to an eager search of an incomplete regex 124 } 125 return []; 126 } 127 }