From c4695bd2b25b9334a8d74a85564701660ba4800d Mon Sep 17 00:00:00 2001 From: siddie-pennewill Date: Wed, 8 Jan 2025 11:44:51 -0500 Subject: [PATCH 1/2] Autocomplete UI Results List Doesn't clear the results list if the user clicks off of it. If the user clicks back on the UI component, the results reappear. --- package-lock.json | 2 +- src/ui/autocomplete.ts | 54 +++++++++++++++++++++++++++--------------- 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/package-lock.json b/package-lock.json index 92f02483..fca2b502 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "radar-sdk-js", - "version": "4.4.6-beta.1", + "version": "4.4.8", "license": "ISC", "dependencies": { "@types/geojson": "^7946.0.10" diff --git a/src/ui/autocomplete.ts b/src/ui/autocomplete.ts index aa090e25..b7daf70b 100644 --- a/src/ui/autocomplete.ts +++ b/src/ui/autocomplete.ts @@ -182,8 +182,10 @@ class AutocompleteUI { // setup event listeners this.inputField.addEventListener('input', this.handleInput.bind(this)); this.inputField.addEventListener('keydown', this.handleKeyboardNavigation.bind(this)); + this.inputField.addEventListener('focus', this.handleFocus.bind(this)); + // Handle blur to hide results, unless clicking on results if (this.config.hideResultsOnBlur) { - this.inputField.addEventListener('blur', this.close.bind(this), true); + this.inputField.addEventListener('blur', this.handleBlur.bind(this), true); } Logger.debug('AutocompleteUI initialized with options', this.config); @@ -213,6 +215,38 @@ class AutocompleteUI { }); } + + + // Add the handleFocus method + public handleFocus() { + if (this.inputField.value.length >= this.config.minCharacters && this.results.length > 0) { + this.displayResults(this.results); // Redisplay previous results + } + } + + // Update the blur handler to ensure it does not prematurely clear results + public handleBlur(event: FocusEvent) { + const relatedTarget = event.relatedTarget as HTMLElement; + const isClickOnResults = relatedTarget && this.resultsList.contains(relatedTarget); + + if (!isClickOnResults) { + this.close(); + } + } + + // Modify the close method to avoid clearing results unnecessarily + public close() { + if (!this.isOpen) { + return; + } + + this.inputField.setAttribute('aria-expanded', 'false'); + this.inputField.setAttribute('aria-activedescendant', ''); + this.resultsList.setAttribute('hidden', ''); + this.highlightedIndex = -1; + this.isOpen = false; + } + public debounce(fn: Function, delay: number) { let timeoutId: any; let resolveFn: any; @@ -357,24 +391,6 @@ class AutocompleteUI { this.isOpen = true; } - public close(e?: FocusEvent) { - if (!this.isOpen) { - return; - } - - // run this code async to allow link click to propagate before blur - // (add 100ms delay if closed from link click) - const linkClick = e && (e.relatedTarget === this.poweredByLink); - setTimeout(() => { - this.inputField.setAttribute('aria-expanded', 'false'); - this.inputField.setAttribute('aria-activedescendant', ''); - this.resultsList.setAttribute('hidden', ''); - this.highlightedIndex = -1; - this.isOpen = false; - this.clearResultsList(); - }, linkClick ? 100 : 0); - } - public goTo(index: number) { if (!this.isOpen || !this.results.length) { return; From 2df09cc601bcefbe0114eb40f27a960da9f387a9 Mon Sep 17 00:00:00 2001 From: siddie-pennewill Date: Thu, 29 May 2025 10:15:40 -0400 Subject: [PATCH 2/2] Add result caching to autocomplete UI Cache autocomplete search results so user can click out of UI component and the results are still there when they click back in. Requested by Jomashop ($12K customer) --- src/ui/autocomplete.ts | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/src/ui/autocomplete.ts b/src/ui/autocomplete.ts index b7daf70b..b42fd2d0 100644 --- a/src/ui/autocomplete.ts +++ b/src/ui/autocomplete.ts @@ -75,6 +75,8 @@ class AutocompleteUI { highlightedIndex: number; debouncedFetchResults: (...args: any[]) => Promise; near?: string; + // ADDED: Cache for storing results by query + private resultsCache: Map = new Map(); // DOM elements container: HTMLElement; @@ -194,17 +196,38 @@ class AutocompleteUI { public handleInput() { // Fetch autocomplete results and display them const query = this.inputField.value; + + // CHANGED: Clear results immediately if query is too short if (query.length < this.config.minCharacters) { + this.clearResultsList(); + this.close(); + return; + } + + // ADDED: Check cache first + const cachedResults = this.resultsCache.get(query); + if (cachedResults) { + const onResults = this.config.onResults; + if (onResults) { + onResults(cachedResults); + } + this.displayResults(cachedResults); return; } this.debouncedFetchResults(query) .then((results: any[]) => { - const onResults = this.config.onResults; - if (onResults) { - onResults(results); + // ADDED: Only display results if the query still matches current input + if (query === this.inputField.value) { + // ADDED: Cache the results + this.resultsCache.set(query, results); + + const onResults = this.config.onResults; + if (onResults) { + onResults(results); + } + this.displayResults(results); } - this.displayResults(results); }) .catch((error) => { Logger.warn(`Autocomplete ui error: ${error.message}`); @@ -234,7 +257,7 @@ class AutocompleteUI { } } - // Modify the close method to avoid clearing results unnecessarily + // CHANGED: Don't clear results, just hide them public close() { if (!this.isOpen) { return; @@ -619,4 +642,4 @@ class AutocompleteUI { } } -export default AutocompleteUI; +export default AutocompleteUI; \ No newline at end of file