-
Notifications
You must be signed in to change notification settings - Fork 3.7k
Merge branch dev with rel-10.1 #24525
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,45 +1,61 @@ | ||||||
| <div class="abp-lookup-container position-relative"> | ||||||
| @if (label()) { | ||||||
| <label class="form-label">{{ label() | abpLocalization }}</label> | ||||||
| <label class="form-label">{{ label() | abpLocalization }}</label> | ||||||
| } | ||||||
|
|
||||||
| <div class="input-group"> | ||||||
| <input type="text" class="form-control" [placeholder]="placeholder() | abpLocalization" [ngModel]="displayValue()" | ||||||
| (ngModelChange)="onSearchInput($event)" (focus)="onSearchFocus()" (blur)="onSearchBlur($event)" | ||||||
| [disabled]="disabled()" /> | ||||||
| <input | ||||||
| type="text" | ||||||
| class="form-control" | ||||||
| [placeholder]="placeholder() | abpLocalization" | ||||||
| [ngModel]="displayValue()" | ||||||
| (ngModelChange)="onSearchInput($event)" | ||||||
| (focus)="onSearchFocus()" | ||||||
| (blur)="onSearchBlur($event)" | ||||||
| [disabled]="disabled()" | ||||||
| /> | ||||||
| @if (displayValue() && !disabled()) { | ||||||
| <button type="button" class="btn btn-outline-secondary" (mousedown)="clearSelection()" tabindex="-1"> | ||||||
| <i class="fa fa-times"></i> | ||||||
| </button> | ||||||
| <button | ||||||
| type="button" | ||||||
| class="btn btn-outline-secondary" | ||||||
| (mousedown)="clearSelection()" | ||||||
| tabindex="-1" | ||||||
| > | ||||||
| <i class="fa fa-times"></i> | ||||||
| </button> | ||||||
| } | ||||||
| </div> | ||||||
|
|
||||||
| @if (showDropdown() && !disabled()) { | ||||||
| <div class="abp-lookup-dropdown list-group position-absolute w-100 shadow"> | ||||||
| @if (isLoading()) { | ||||||
| <div class="list-group-item text-center py-3"> | ||||||
| <i class="fa fa-spinner fa-spin me-2"></i> | ||||||
| {{ 'AbpUi::Loading' | abpLocalization }} | ||||||
| </div> | ||||||
| } @else if (searchResults().length > 0) { | ||||||
| @for (item of searchResults(); track item.key) { | ||||||
| <button type="button" class="list-group-item list-group-item-action" (mousedown)="selectItem(item)"> | ||||||
| @if (itemTemplate()) { | ||||||
| <ng-container *ngTemplateOutlet="itemTemplate()!; context: { $implicit: item }" /> | ||||||
| } @else { | ||||||
| {{ getDisplayValue(item) }} | ||||||
| <div class="abp-lookup-dropdown list-group position-absolute w-100"> | ||||||
| @if (isLoading()) { | ||||||
| <div class="list-group-item text-center py-3"> | ||||||
| <i class="fa fa-spinner fa-spin me-2"></i> | ||||||
| {{ 'AbpUi::Loading' | abpLocalization }} | ||||||
| </div> | ||||||
| } @else if (searchResults().length > 0) { | ||||||
| @for (item of searchResults(); track item.key) { | ||||||
| <button | ||||||
| type="button" | ||||||
| class="list-group-item list-group-item-action" | ||||||
| (mousedown)="selectItem(item)" | ||||||
| > | ||||||
| @if (itemTemplate()) { | ||||||
| <ng-container *ngTemplateOutlet="itemTemplate()!; context: { $implicit: item }" /> | ||||||
| } @else { | ||||||
| {{ getDisplayValue(item) }} | ||||||
| } | ||||||
| </button> | ||||||
| } | ||||||
| } @else if (displayValue()) { | ||||||
| @if (noResultsTemplate()) { | ||||||
| <ng-container *ngTemplateOutlet="noResultsTemplate()!" /> | ||||||
| } @else { | ||||||
| <div class="list-group-item text-muted"> | ||||||
| {{ 'AbpUi::NoDataAvailableInDatatable' | abpLocalization }} | ||||||
|
||||||
| {{ 'AbpUi::NoDataAvailableInDatatable' | abpLocalization }} | |
| {{ 'AbpUi::NoRecordsFound' | abpLocalization }} |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,5 +1,8 @@ | ||||||
| .abp-lookup-dropdown { | ||||||
| z-index: 1050; | ||||||
| max-height: 200px; | ||||||
| overflow-y: auto; | ||||||
| z-index: 1060; | ||||||
|
||||||
| z-index: 1060; | |
| z-index: 1050; |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,15 +1,15 @@ | ||||||
| import { | ||||||
| Component, | ||||||
| input, | ||||||
| output, | ||||||
| model, | ||||||
| signal, | ||||||
| OnInit, | ||||||
| ChangeDetectionStrategy, | ||||||
| TemplateRef, | ||||||
| contentChild, | ||||||
| DestroyRef, | ||||||
| inject, | ||||||
| Component, | ||||||
| input, | ||||||
| output, | ||||||
| model, | ||||||
| signal, | ||||||
| OnInit, | ||||||
| ChangeDetectionStrategy, | ||||||
| TemplateRef, | ||||||
| contentChild, | ||||||
| DestroyRef, | ||||||
| inject, | ||||||
| } from '@angular/core'; | ||||||
| import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; | ||||||
| import { CommonModule } from '@angular/common'; | ||||||
|
|
@@ -18,119 +18,123 @@ import { LocalizationPipe } from '@abp/ng.core'; | |||||
| import { Subject, Observable, debounceTime, distinctUntilChanged, of, finalize } from 'rxjs'; | ||||||
|
|
||||||
| export interface LookupItem { | ||||||
| key: string; | ||||||
| displayName: string; | ||||||
| [key: string]: unknown; | ||||||
| key: string; | ||||||
| displayName: string; | ||||||
| [key: string]: unknown; | ||||||
| } | ||||||
|
|
||||||
| export type LookupSearchFn<T = LookupItem> = (filter: string) => Observable<T[]>; | ||||||
|
|
||||||
| @Component({ | ||||||
| selector: 'abp-lookup-search', | ||||||
| templateUrl: './lookup-search.component.html', | ||||||
| styleUrl: './lookup-search.component.scss', | ||||||
| imports: [CommonModule, FormsModule, LocalizationPipe], | ||||||
| changeDetection: ChangeDetectionStrategy.OnPush, | ||||||
| selector: 'abp-lookup-search', | ||||||
| templateUrl: './lookup-search.component.html', | ||||||
| styleUrl: './lookup-search.component.scss', | ||||||
| imports: [CommonModule, FormsModule, LocalizationPipe], | ||||||
| changeDetection: ChangeDetectionStrategy.OnPush, | ||||||
| }) | ||||||
| export class LookupSearchComponent<T extends LookupItem = LookupItem> implements OnInit { | ||||||
| private readonly destroyRef = inject(DestroyRef); | ||||||
|
|
||||||
| readonly label = input<string>(); | ||||||
| readonly placeholder = input<string>(''); | ||||||
| readonly debounceTime = input<number>(300); | ||||||
| readonly minSearchLength = input<number>(0); | ||||||
| readonly displayKey = input<keyof T>('displayName' as keyof T); | ||||||
| readonly valueKey = input<keyof T>('key' as keyof T); | ||||||
| readonly disabled = input<boolean>(false); | ||||||
|
|
||||||
| readonly searchFn = input<LookupSearchFn<T>>(() => of([])); | ||||||
|
|
||||||
| readonly selectedValue = model<string>(''); | ||||||
| readonly displayValue = model<string>(''); | ||||||
|
|
||||||
| readonly itemSelected = output<T>(); | ||||||
| readonly searchChanged = output<string>(); | ||||||
|
|
||||||
| readonly itemTemplate = contentChild<TemplateRef<{ $implicit: T }>>('itemTemplate'); | ||||||
| readonly noResultsTemplate = contentChild<TemplateRef<void>>('noResultsTemplate'); | ||||||
|
|
||||||
| readonly searchResults = signal<T[]>([]); | ||||||
| readonly showDropdown = signal(false); | ||||||
| readonly isLoading = signal(false); | ||||||
|
|
||||||
| private readonly searchSubject = new Subject<string>(); | ||||||
|
|
||||||
| ngOnInit() { | ||||||
| this.searchSubject.pipe( | ||||||
| debounceTime(this.debounceTime()), | ||||||
| distinctUntilChanged(), | ||||||
| takeUntilDestroyed(this.destroyRef) | ||||||
| ).subscribe(filter => { | ||||||
| this.performSearch(filter); | ||||||
| }); | ||||||
| } | ||||||
|
|
||||||
| onSearchInput(filter: string) { | ||||||
| this.displayValue.set(filter); | ||||||
| this.showDropdown.set(true); | ||||||
| this.searchChanged.emit(filter); | ||||||
|
|
||||||
| if (filter.length >= this.minSearchLength()) { | ||||||
| this.searchSubject.next(filter); | ||||||
| } else { | ||||||
| this.searchResults.set([]); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| onSearchFocus() { | ||||||
| this.showDropdown.set(true); | ||||||
| const currentFilter = this.displayValue() || ''; | ||||||
| if (currentFilter.length >= this.minSearchLength()) { | ||||||
| this.performSearch(currentFilter); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| onSearchBlur(event: FocusEvent) { | ||||||
| const relatedTarget = event.relatedTarget as HTMLElement; | ||||||
| if (!relatedTarget?.closest('.abp-lookup-dropdown')) { | ||||||
| this.showDropdown.set(false); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| selectItem(item: T) { | ||||||
| const displayKeyValue = String(item[this.displayKey()] ?? ''); | ||||||
| const valueKeyValue = String(item[this.valueKey()] ?? ''); | ||||||
|
|
||||||
| this.displayValue.set(displayKeyValue); | ||||||
| this.selectedValue.set(valueKeyValue); | ||||||
| this.searchResults.set([]); | ||||||
| this.showDropdown.set(false); | ||||||
| this.itemSelected.emit(item); | ||||||
| } | ||||||
|
|
||||||
| clearSelection() { | ||||||
| this.displayValue.set(''); | ||||||
| this.selectedValue.set(''); | ||||||
| this.searchResults.set([]); | ||||||
| private readonly destroyRef = inject(DestroyRef); | ||||||
|
|
||||||
| readonly label = input<string>(); | ||||||
| readonly placeholder = input<string>(''); | ||||||
| readonly debounceTime = input<number>(300); | ||||||
| readonly minSearchLength = input<number>(0); | ||||||
| readonly displayKey = input<keyof T>('displayName' as keyof T); | ||||||
| readonly valueKey = input<keyof T>('key' as keyof T); | ||||||
| readonly disabled = input<boolean>(false); | ||||||
|
|
||||||
| readonly searchFn = input<LookupSearchFn<T>>(() => of([])); | ||||||
|
|
||||||
| readonly selectedValue = model<string>(''); | ||||||
| readonly displayValue = model<string>(''); | ||||||
|
|
||||||
| readonly itemSelected = output<T>(); | ||||||
| readonly searchChanged = output<string>(); | ||||||
|
|
||||||
| readonly itemTemplate = contentChild<TemplateRef<{ $implicit: T }>>('itemTemplate'); | ||||||
| readonly noResultsTemplate = contentChild<TemplateRef<void>>('noResultsTemplate'); | ||||||
|
|
||||||
| readonly searchResults = signal<T[]>([]); | ||||||
| readonly showDropdown = signal(true); | ||||||
|
||||||
| readonly showDropdown = signal(true); | |
| readonly showDropdown = signal(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The removal of the 'shadow' CSS class from the dropdown may affect visual consistency if other dropdowns in the application use shadows. Additionally, the new 'background-color: var(--lpx-content-bg)' CSS variable may not be defined in all themes, which could result in the dropdown having a transparent or incorrect background. Verify that this CSS variable is defined across all supported themes.