From 3962c2bb0435484b60a3e408e4738d792e249a53 Mon Sep 17 00:00:00 2001
From: buli <137736985@qq.com>
Date: 星期一, 05 六月 2023 11:09:55 +0800
Subject: [PATCH] LEX CommunityNewCmp

---
 force-app/main/default/lwc/lexLookup/lexLookup.js |  493 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 493 insertions(+), 0 deletions(-)

diff --git a/force-app/main/default/lwc/lexLookup/lexLookup.js b/force-app/main/default/lwc/lexLookup/lexLookup.js
new file mode 100644
index 0000000..7d2ef23
--- /dev/null
+++ b/force-app/main/default/lwc/lexLookup/lexLookup.js
@@ -0,0 +1,493 @@
+import { LightningElement, api, track } from 'lwc';
+import { NavigationMixin } from 'lightning/navigation';
+
+const SEARCH_DELAY = 300; // Wait 300 ms after user stops typing then, peform search
+
+const KEY_ARROW_UP = 38;
+const KEY_ARROW_DOWN = 40;
+const KEY_ENTER = 13;
+
+const VARIANT_LABEL_STACKED = 'label-stacked';
+const VARIANT_LABEL_INLINE = 'label-inline';
+const VARIANT_LABEL_HIDDEN = 'label-hidden';
+
+const REGEX_SOSL_RESERVED = /(\?|&|\||!|\{|\}|\[|\]|\(|\)|\^|~|\*|:|"|\+|-|\\)/g;
+const REGEX_EXTRA_TRAP = /(\$|\\)/g;
+
+export default class LexLookup extends NavigationMixin(LightningElement) {
+    // Public properties
+    @api variant = VARIANT_LABEL_STACKED;
+    @api label = '';
+    @api required = false;
+    @api disabled = false;
+    @api placeholder = '';
+    @api isMultiEntry = false;
+    @api scrollAfterNItems = null;
+    @api newRecordOptions = [];
+    @api minSearchTermLength = 2;
+    @api isDisabledForDealerText = false;
+    @api accountValue = '';
+
+    // Template properties
+    searchResultsLocalState = [];
+    loading = false;
+
+    // Private properties
+    _errors = [];
+    _hasFocus = false;
+    _isDirty = false;
+    _searchTerm = '';
+    _cleanSearchTerm;
+    _cancelBlur = false;
+    _searchThrottlingTimeout;
+    _searchResults = [];
+    _defaultSearchResults = [];
+    _curSelection = [];
+    _focusedResultIndex = null;
+
+
+
+
+
+    // PUBLIC FUNCTIONS AND GETTERS/SETTERS
+    @api
+    set selection(initialSelection) {
+        if (initialSelection) {
+            this._curSelection = Array.isArray(initialSelection) ? initialSelection : [initialSelection];
+            this.processSelectionUpdate(false);
+        }
+    }
+
+    get selection() {
+        return this._curSelection;
+    }
+
+    @api
+    set errors(errors) {
+        this._errors = errors;
+        // Blur component if errors are passed
+        if (this._errors?.length > 0) {
+            this.blur();
+        }
+    }
+
+    get errors() {
+        return this._errors;
+    }
+
+    @api
+    get validity() {
+        return { valid: !this._errors || this._errors.length === 0 };
+    }
+
+    @api
+    get value() {
+        return this.getSelection();
+    }
+
+    @api
+    setSearchResults(results) {
+        // Reset the spinner
+        this.loading = false;
+        // Clone results before modifying them to avoid Locker restriction
+        let resultsLocal = JSON.parse(JSON.stringify(results));
+        // Remove selected items from search results
+        const selectedIds = this._curSelection.map((sel) => sel.id);
+        resultsLocal = resultsLocal.filter((result) => selectedIds.indexOf(result.id) === -1);
+        // Format results
+        const cleanSearchTerm = this._searchTerm.replace(REGEX_SOSL_RESERVED, '.?').replace(REGEX_EXTRA_TRAP, '\\$1');
+        const regex = new RegExp(`(${cleanSearchTerm})`, 'gi');
+        this._searchResults = resultsLocal.map((result) => {
+            // Format title and subtitle
+            if (this._searchTerm.length > 0) {
+                result.titleFormatted = result.title
+                    ? result.title.replace(regex, '<strong>$1</strong>')
+                    : result.title;
+                result.subtitleFormatted = result.subtitle
+                    ? result.subtitle.replace(regex, '<strong>$1</strong>')
+                    : result.subtitle;
+            } else {
+                result.titleFormatted = result.title;
+                result.subtitleFormatted = result.subtitle;
+            }
+            // Add icon if missing
+            if (typeof result.icon === 'undefined') {
+                result.icon = 'standard:default';
+            }
+            return result;
+        });
+        // Add local state and dynamic class to search results
+        this._focusedResultIndex = null;
+        const self = this;
+        this.searchResultsLocalState = this._searchResults.map((result, i) => {
+            return {
+                result,
+                state: {},
+                get classes() {
+                    let cls = 'slds-media slds-media_center slds-listbox__option slds-listbox__option_entity';
+                    if (result.subtitleFormatted) {
+                        cls += ' slds-listbox__option_has-meta';
+                    }
+                    if (self._focusedResultIndex === i) {
+                        cls += ' slds-has-focus';
+                    }
+                    return cls;
+                }
+            };
+        });
+    }
+
+    @api
+    getSelection() {
+        console.log('get selection:' +this._curSelection);
+        return this._curSelection;
+    }
+
+    @api
+    setDefaultResults(results) {
+        this._defaultSearchResults = [...results];
+        if (this._searchResults.length === 0) {
+            this.setSearchResults(this._defaultSearchResults);
+        }
+    }
+
+    @api
+    blur() {
+        this.template.querySelector('input')?.blur();
+    }
+
+    connectedCallback(){
+        console.log('LexLookup accountValue = ' + this.accountValue);
+        console.log('isDisabledForDealerText = ' + this.isDisabledForDealerText);
+    }
+
+    // INTERNAL FUNCTIONS
+    updateSearchTerm(newSearchTerm) {
+        this._searchTerm = newSearchTerm;
+
+        // Compare clean new search term with current one and abort if identical
+        const newCleanSearchTerm = newSearchTerm.trim().replace(REGEX_SOSL_RESERVED, '?').toLowerCase();
+        if (this._cleanSearchTerm === newCleanSearchTerm) {
+            return;
+        }
+
+        // Save clean search term
+        this._cleanSearchTerm = newCleanSearchTerm;
+
+        // Ignore search terms that are too small after removing special characters
+        if (newCleanSearchTerm.replace(/\?/g, '').length < this.minSearchTermLength) {
+            this.setSearchResults(this._defaultSearchResults);
+            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 >= this.minSearchTermLength) {
+                // Display spinner until results are returned
+                this.loading = true;
+
+                const searchEvent = new CustomEvent('search', {
+                    detail: {
+                        searchTerm: this._cleanSearchTerm,
+                        rawSearchTerm: newSearchTerm,
+                        selectedIds: this._curSelection.map((element) => element.id)
+                    }
+                });
+                this.dispatchEvent(searchEvent);
+            }
+            this._searchThrottlingTimeout = null;
+        }, SEARCH_DELAY);
+    }
+
+    isSelectionAllowed() {
+        if (this.isMultiEntry) {
+            return true;
+        }
+        return !this.hasSelection();
+    }
+
+    hasSelection() {
+        return this._curSelection.length > 0;
+    }
+
+    processSelectionUpdate(isUserInteraction) {
+        // Reset search
+        this._cleanSearchTerm = '';
+        this._searchTerm = '';
+        this.setSearchResults([...this._defaultSearchResults]);
+        // Indicate that component was interacted with
+        this._isDirty = isUserInteraction;
+        // Blur input after single select lookup selection
+        if (!this.isMultiEntry && this.hasSelection()) {
+            this._hasFocus = false;
+        }
+        // If selection was changed by user, notify parent components
+        if (isUserInteraction) {
+            const selectedIds = this._curSelection.map((sel) => sel.id);
+            this.dispatchEvent(new CustomEvent('selectionchange', { detail: selectedIds }));
+        }
+    }
+
+    // EVENT HANDLING
+
+    handleInput(event) {
+        // Prevent action if selection is not allowed
+        if (!this.isSelectionAllowed()) {
+            return;
+        }
+        this.updateSearchTerm(event.target.value);
+    }
+
+    handleKeyDown(event) {
+        if (this._focusedResultIndex === null) {
+            this._focusedResultIndex = -1;
+        }
+        if (event.keyCode === KEY_ARROW_DOWN) {
+            // If we hit 'down', select the next item, or cycle over.
+            this._focusedResultIndex++;
+            if (this._focusedResultIndex >= this._searchResults.length) {
+                this._focusedResultIndex = 0;
+            }
+            event.preventDefault();
+        } else if (event.keyCode === KEY_ARROW_UP) {
+            // If we hit 'up', select the previous item, or cycle over.
+            this._focusedResultIndex--;
+            if (this._focusedResultIndex < 0) {
+                this._focusedResultIndex = this._searchResults.length - 1;
+            }
+            event.preventDefault();
+        } else if (event.keyCode === KEY_ENTER && this._hasFocus && this._focusedResultIndex >= 0) {
+            // If the user presses enter, and the box is open, and we have used arrows,
+            // treat this just like a click on the listbox item
+            const selectedId = this._searchResults[this._focusedResultIndex].id;
+            console.log('selectedid:'+selectedId);
+            this.template.querySelector(`[data-recordid="${selectedId}"]`).click();
+            event.preventDefault();
+        }
+    }
+
+    handleResultClick(event) {
+        const recordId = event.currentTarget.dataset.recordid;
+
+        // Save selection
+        const selectedItem = this._searchResults.find((result) => result.id === recordId);
+        if (!selectedItem) {
+            return;
+        }
+        const newSelection = [...this._curSelection];
+        newSelection.push(selectedItem);
+        this._curSelection = newSelection;
+
+        // Process selection update
+        this.processSelectionUpdate(true);
+    }
+
+    handleComboboxMouseDown(event) {
+        const mainButton = 0;
+        if (event.button === mainButton) {
+            this._cancelBlur = true;
+        }
+    }
+
+    handleComboboxMouseUp() {
+        this._cancelBlur = false;
+        // Re-focus to text input for the next blur event
+        this.template.querySelector('input').focus();
+    }
+
+    handleFocus() {
+        // Prevent action if selection is not allowed
+        if (!this.isSelectionAllowed()) {
+            return;
+        }
+        this._hasFocus = true;
+        this._focusedResultIndex = null;
+    }
+
+    handleBlur() {
+        // Prevent action if selection is either not allowed or cancelled
+        if (!this.isSelectionAllowed() || this._cancelBlur) {
+            return;
+        }
+        const blurEvent = new CustomEvent('blur', {
+            detail: {}
+        });
+        this.dispatchEvent(blurEvent);
+        this._hasFocus = false;
+        if(!this.hasSelection()){
+            this._searchTerm = '';
+        }
+    }
+
+    handleRemoveSelectedItem(event) {
+        if (this.disabled) {
+            return;
+        }
+        const recordId = event.currentTarget.name;
+        this._curSelection = this._curSelection.filter((item) => item.id !== recordId);
+        // Process selection update
+        this.processSelectionUpdate(true);
+    }
+
+    handleClearSelection() {
+        this._curSelection = [];
+        this._hasFocus = false;
+        this.accountValue = '';
+        // Process selection update
+        this.processSelectionUpdate(true);
+    }
+
+    handleNewRecordClick(event) {
+        const objectApiName = event.currentTarget.dataset.sobject;
+        const selection = this.newRecordOptions.find((option) => option.value === objectApiName);
+
+        const preNavigateCallback = selection.preNavigateCallback
+            ? selection.preNavigateCallback
+            : () => Promise.resolve();
+        preNavigateCallback(selection).then(() => {
+            this[NavigationMixin.Navigate]({
+                type: 'standard__objectPage',
+                attributes: {
+                    objectApiName,
+                    actionName: 'new'
+                },
+                state: {
+                    defaultFieldValues: selection.defaults
+                }
+            });
+        });
+    }
+
+    // STYLE EXPRESSIONS
+
+    get isSingleEntry() {
+        return !this.isMultiEntry;
+    }
+
+    get isListboxOpen() {
+        const isSearchTermValid = this._cleanSearchTerm && this._cleanSearchTerm.length >= this.minSearchTermLength;
+        return (
+            this._hasFocus &&
+            this.isSelectionAllowed() &&
+            (isSearchTermValid || this.hasResults || this.newRecordOptions?.length > 0)
+        );
+    }
+
+    get hasResults() {
+        return this._searchResults.length > 0;
+    }
+
+    get getFormElementClass() {
+        return this.variant === VARIANT_LABEL_INLINE
+            ? 'slds-form-element slds-form-element_horizontal'
+            : 'slds-form-element';
+    }
+
+    get getLabelClass() {
+        return this.variant === VARIANT_LABEL_HIDDEN
+            ? 'slds-form-element__label slds-assistive-text'
+            : 'slds-form-element__label';
+    }
+
+    get getContainerClass() {
+        let css = 'slds-combobox_container ';
+        if (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.isListboxOpen) {
+            css += 'slds-is-open';
+        }
+        return css;
+    }
+
+    get getInputClass() {
+        let css = 'slds-input slds-combobox__input has-custom-height ';
+        if (this._hasFocus && this.hasResults) {
+            css += 'slds-has-focus ';
+        }
+        if (this._errors.length > 0 || (this._isDirty && this.required && !this.hasSelection())) {
+            css += '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() {
+        if(this._curSelection[0])
+            console.log('this._curSelection[0].icon = ' + this._curSelection[0].icon);
+        return this.hasSelection() ? this._curSelection[0].icon : 'standard:default';
+    }
+
+    get getSelectIconClass() {
+        return 'slds-combobox__input-entity-icon ' + (this.hasSelection() ? '' : 'slds-hide');
+    }
+
+    get getInputValue() {
+        if (this.isMultiEntry) {
+            return this._searchTerm;
+        }
+        if(this.accountValue != ''){
+            return this.accountValue;
+        }
+        return this.hasSelection() ? this._curSelection[0].title : this._searchTerm;
+    }
+
+    get getInputTitle() {
+        if (this.isMultiEntry) {
+            return '';
+        }
+        return this.hasSelection() ? this._curSelection[0].title : '';
+    }
+
+    get getListboxClass() {
+        return (
+            'slds-dropdown ' +
+            (this.scrollAfterNItems ? `slds-dropdown_length-with-icon-${this.scrollAfterNItems} ` : '') +
+            'slds-dropdown_fluid'
+        );
+    }
+
+    get isInputReadonly() {
+        if (this.isMultiEntry) {
+            return false;
+        }
+        return this.hasSelection();
+    }
+}
\ No newline at end of file

--
Gitblit v1.9.1