fonteva-design-guide
Version:
## Dev, Build and Test
346 lines (301 loc) • 11.1 kB
JavaScript
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 {
get selection() {
return this._selection;
}
set selection(value) {
this._selection = value ? value : [];
}
placeholder = '';
isMultiEntry = false;
errors = [];
scrollAfterNItems;
customKey;
query;
rowId;
rowTitle;
rowSubtitle;
rowIcon;
searchTerm = '';
searchResults = [];
hasFocus = false;
_selection = [];
cleanSearchTerm;
blurTimeout;
searchThrottlingTimeout;
// EXPOSED FUNCTIONS
updateValue(record, noChangeEvent) {
if (!record) {
return;
}
const resultRecord = this.buildResultRecord(record);
this.setSearchResults([resultRecord]);
this.handleResultClick({
ignoreDispatchEvent: noChangeEvent,
currentTarget: {
dataset: {
recordid: resultRecord.id
}
}
});
}
setSearchResults(results) {
this.searchResults = results.map(result => {
if (typeof result.icon === 'undefined') {
result.icon = 'standard:default';
}
return result;
});
}
getSelection() {
return this.selection;
}
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();
}
}