From 42198affd63b6a0939599daac34abbf1e13f9875 Mon Sep 17 00:00:00 2001 From: adityagarud Date: Thu, 2 Oct 2025 07:49:15 +0530 Subject: [PATCH] feat: auto-preselect first search result in assistant --- .../src/lib/assistant/assistant.component.ts | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/libs/ui/src/lib/assistant/assistant.component.ts b/libs/ui/src/lib/assistant/assistant.component.ts index e5d0dd6da..9031b2ed3 100644 --- a/libs/ui/src/lib/assistant/assistant.component.ts +++ b/libs/ui/src/lib/assistant/assistant.component.ts @@ -177,8 +177,11 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { 'TAG' ]; private keyManager: FocusKeyManager; + private preselectionTimeout: any; private unsubscribeSubject = new Subject(); + private readonly PRESELECTION_DELAY = 100; + public constructor( private adminService: AdminService, private changeDetectorRef: ChangeDetectorRef, @@ -345,6 +348,9 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { next: (searchResults) => { this.searchResults = searchResults; this.changeDetectorRef.markForCheck(); + + // Trigger preselection of first item + this.preselectFirstItem(); }, error: (error) => { console.error('Assistant search stream error:', error); @@ -585,6 +591,11 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { } public ngOnDestroy() { + // Clear preselection timeout + if (this.preselectionTimeout) { + clearTimeout(this.preselectionTimeout); + } + this.unsubscribeSubject.next(); this.unsubscribeSubject.complete(); } @@ -595,6 +606,66 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { }); } + /** + * Gets the first search result item based on priority order: + * Quick Links → Accounts → Holdings → Asset Profiles + */ + private getFirstSearchResultItem(): ISearchResultItem | null { + // Priority order: Quick Links → Accounts → Holdings → Asset Profiles + if (this.searchResults.quickLinks?.length > 0) { + return this.searchResults.quickLinks[0]; + } + if (this.searchResults.accounts?.length > 0) { + return this.searchResults.accounts[0]; + } + if (this.searchResults.holdings?.length > 0) { + return this.searchResults.holdings[0]; + } + if (this.searchResults.assetProfiles?.length > 0) { + return this.searchResults.assetProfiles[0]; + } + return null; + } + + /** + * Preselects the first search result item with debouncing + */ + private preselectFirstItem(): void { + // Clear any existing timeout + if (this.preselectionTimeout) { + clearTimeout(this.preselectionTimeout); + } + + // Debounce preselection to handle rapid search changes + this.preselectionTimeout = setTimeout(() => { + // Check if we have search results and the assistant is open + if (!this.isOpen || !this.searchFormControl.value) { + return; + } + + const firstItem = this.getFirstSearchResultItem(); + if (!firstItem) { + return; + } + + // Clear any existing focus + for (const item of this.assistantListItems) { + item.removeFocus(); + } + + // Set the first item as active in the key manager + this.keyManager.setFirstItemActive(); + + // Get the currently focused item and apply focus styling + const currentFocusedItem = this.getCurrentAssistantListItem(); + if (currentFocusedItem) { + currentFocusedItem.focus(); + } + + this.changeDetectorRef.markForCheck(); + }, this.PRESELECTION_DELAY); + } + private searchAccounts(aSearchTerm: string): Observable { return this.dataService .fetchAccounts({