UNPKG

fonteva-design-guide

Version:

## Dev, Build and Test

346 lines (301 loc) 11.1 kB
import { LightningElement, track, api } from 'lwc'; import apexSearch from '@salesforce/apex/pfmInputController.search'; const MINIMAL_SEARCH_TERM_LENGTH = 2; // Min number of chars required to search const SEARCH_DELAY = 500; // Wait 500 ms after user stops typing then, perform search export default class PfmInputLookup extends LightningElement { @api get selection() { return this._selection; } set selection(value) { this._selection = value ? value : []; } @api placeholder = ''; @api isMultiEntry = false; @api errors = []; @api scrollAfterNItems; @api customKey; @api query; @api rowId; @api rowTitle; @api rowSubtitle; @api rowIcon; @track searchTerm = ''; @track searchResults = []; @track hasFocus = false; @track _selection = []; cleanSearchTerm; blurTimeout; searchThrottlingTimeout; // EXPOSED FUNCTIONS @api updateValue(record, noChangeEvent) { if (!record) { return; } const resultRecord = this.buildResultRecord(record); this.setSearchResults([resultRecord]); this.handleResultClick({ ignoreDispatchEvent: noChangeEvent, currentTarget: { dataset: { recordid: resultRecord.id } } }); } @api setSearchResults(results) { this.searchResults = results.map(result => { if (typeof result.icon === 'undefined') { result.icon = 'standard:default'; } return result; }); } @api getSelection() { return this.selection; } @api getkey() { return this.customKey; } // INTERNAL FUNCTIONS updateSearchTerm(newSearchTerm) { let self = this; this.searchTerm = newSearchTerm; // Compare clean new search term with current one and abort if identical const newCleanSearchTerm = newSearchTerm.trim().replace(/\*/g, '').toLowerCase(); if (this.cleanSearchTerm === newCleanSearchTerm) { return; } // Save clean search term this.cleanSearchTerm = newCleanSearchTerm; // Ignore search terms that are too small if (newCleanSearchTerm.length < MINIMAL_SEARCH_TERM_LENGTH) { this.searchResults = []; return; } // Apply search throttling (prevents search if user is still typing) if (this.searchThrottlingTimeout) { clearTimeout(this.searchThrottlingTimeout); } // eslint-disable-next-line @lwc/lwc/no-async-operation this.searchThrottlingTimeout = setTimeout(() => { // Send search event if search term is long enougth if (this.cleanSearchTerm.length >= MINIMAL_SEARCH_TERM_LENGTH) { if (this.query) { apexSearch({ searchTerm: this.cleanSearchTerm, searchQuery: this.query, selectedIds: this.selection.map(element => element.id) }) .then(results => { if (this.query) { this.setSearchResults( results.map(r => { return self.buildResultRecord(r, self); }) ); } else { this.setSearchResults(results); } }) .catch(error => { // this.notifyUser('Lookup Error', 'An error occured while searching with the lookup field.', 'error'); // eslint-disable-next-line no-console // TODO: show error with toast console.error('Lookup error', JSON.stringify(error)); this.errors = [error]; }); } else { const searchEvent = new CustomEvent('search', { detail: { searchTerm: this.cleanSearchTerm, selectedIds: this.selection.map(element => element.id) } }); this.dispatchEvent(searchEvent); } } this.searchThrottlingTimeout = null; }, SEARCH_DELAY); } buildResultRecord(r, self) { if (!self) { self = this; } return { id: self.rowId ? (typeof self.rowId === 'function' ? self.rowId(r) : r[self.rowId]) : r.Id, sObjectType: 'Account', title: self.rowTitle ? (typeof self.rowTitle === 'function' ? self.rowTitle(r) : r[self.rowTitle]) : r.Name, subtitle: self.rowSubtitle ? typeof self.rowSubtitle === 'function' ? self.rowSubtitle(r) : r[self.rowSubtitle] : null, icon: self.rowIcon ? (typeof self.rowIcon === 'function' ? self.rowIcon(r) : self.rowIcon) : null }; } isSelectionAllowed() { if (this.isMultiEntry) { return true; } return !this.hasSelection(); } hasResults() { return this.searchResults.length > 0; } hasSelection() { return this.selection.length > 0; } // EVENT HANDLING handleInput(event) { // Prevent action if selection is not allowed if (!this.isSelectionAllowed()) { return; } this.updateSearchTerm(event.target.value); } handleResultClick(event) { const recordId = event.currentTarget.dataset.recordid; // Save selection let selectedItem = this.searchResults.filter(result => result.id === recordId); if (selectedItem.length === 0) { return; } selectedItem = selectedItem[0]; const newSelection = [...this.selection]; newSelection.push(selectedItem); this.selection = newSelection; // Reset search this.searchTerm = ''; this.searchResults = []; // Notify parent components that selection has changed if (!event.ignoreDispatchEvent) { this.dispatchEvent( new CustomEvent('selectionchange', { detail: { value: selectedItem } }) ); } } handleComboboxClick() { // Hide combobox immediatly if (this.blurTimeout) { window.clearTimeout(this.blurTimeout); } this.hasFocus = false; } handleFocus() { // Prevent action if selection is not allowed if (!this.isSelectionAllowed()) { return; } this.hasFocus = true; } handleBlur() { // Prevent action if selection is not allowed if (!this.isSelectionAllowed()) { return; } // Delay hiding combobox so that we can capture selected result // eslint-disable-next-line @lwc/lwc/no-async-operation this.blurTimeout = window.setTimeout(() => { this.hasFocus = false; this.blurTimeout = null; }, 300); } handleRemoveSelectedItem(event) { const recordId = event.currentTarget.name; this.selection = this.selection.filter(item => item.id !== recordId); // Notify parent components that selection has changed this.dispatchEvent(new CustomEvent('selectionchange')); } handleClearSelection() { this.selection = []; // Notify parent components that selection has changed this.dispatchEvent(new CustomEvent('selectionchange')); } // STYLE EXPRESSIONS get getContainerClass() { let css = 'slds-combobox_container slds-has-inline-listbox '; if (this.hasFocus && this.hasResults()) { css += 'slds-has-input-focus '; } if (this.errors && this.errors.length > 0) { css += 'has-custom-error'; } return css; } get getDropdownClass() { let css = 'slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click '; if (this.hasFocus && this.hasResults()) { css += 'slds-is-open'; } else { css += 'slds-combobox-lookup'; } return css; } get getInputClass() { let css = 'slds-input slds-combobox__input has-custom-height ' + (!this.errors || this.errors.length === 0 ? '' : 'has-custom-error '); if (!this.isMultiEntry) { css += 'slds-combobox__input-value ' + (this.hasSelection() ? 'has-custom-border' : ''); } return css; } get getComboboxClass() { let css = 'slds-combobox__form-element slds-input-has-icon '; if (this.isMultiEntry) { css += 'slds-input-has-icon_right'; } else { css += this.hasSelection() ? 'slds-input-has-icon_left-right' : 'slds-input-has-icon_right'; } return css; } get getSearchIconClass() { let css = 'slds-input__icon slds-input__icon_right '; if (!this.isMultiEntry) { css += this.hasSelection() ? 'slds-hide' : ''; } return css; } get getClearSelectionButtonClass() { return ( 'slds-button slds-button_icon slds-input__icon slds-input__icon_right ' + (this.hasSelection() ? '' : 'slds-hide') ); } get getSelectIconName() { return this.hasSelection() ? this.selection[0].icon : 'standard:default'; } get getSelectIconClass() { return 'slds-combobox__input-entity-icon ' + (this.hasSelection() ? '' : 'slds-hide'); } get getInputValue() { if (this.isMultiEntry) { return this.searchTerm; } return this.hasSelection() ? this.selection[0].title : this.searchTerm; } get getListboxClass() { return ( 'slds-listbox slds-listbox_vertical slds-dropdown slds-dropdown_fluid ' + (this.scrollAfterNItems ? 'slds-dropdown_length-with-icon-' + this.scrollAfterNItems : '') ); } get isInputReadonly() { if (this.isMultiEntry) { return false; } return this.hasSelection(); } get isExpanded() { return this.hasResults(); } }