diff --git a/ui/src/app/schema-form/widget/filter-target/filter-target.component.html b/ui/src/app/schema-form/widget/filter-target/filter-target.component.html
index 059d0acdd..005ae6e37 100644
--- a/ui/src/app/schema-form/widget/filter-target/filter-target.component.html
+++ b/ui/src/app/schema-form/widget/filter-target/filter-target.component.html
@@ -52,7 +52,8 @@
class="component-control"
limit="10"
[formControl]="search"
- [matches]="ids"
+ [matches]="ids$ | async"
+ [count]="idCount$ | async"
(keydown.enter)="onSelectValue(search.value)">
diff --git a/ui/src/app/schema-form/widget/filter-target/filter-target.component.ts b/ui/src/app/schema-form/widget/filter-target/filter-target.component.ts
index 835e2f005..7eed2fca9 100644
--- a/ui/src/app/schema-form/widget/filter-target/filter-target.component.ts
+++ b/ui/src/app/schema-form/widget/filter-target/filter-target.component.ts
@@ -3,7 +3,7 @@ import { FormControl, Validators, AbstractControl, ValidatorFn } from '@angular/
import { ObjectWidget } from 'ngx-schema-form';
import { Store } from '@ngrx/store';
import { Observable, Subject } from 'rxjs';
-import { distinctUntilChanged, skipWhile, takeUntil, map } from 'rxjs/operators';
+import { distinctUntilChanged, skipWhile, takeUntil, map, withLatestFrom, filter, switchMap, startWith } from 'rxjs/operators';
import * as fromRoot from '../../../app.reducer';
import * as fromFilters from '../../../metadata/filter/reducer';
@@ -19,7 +19,7 @@ import { QueryEntityIds, ClearSearch } from '../../../metadata/filter/action/sea
export class FilterTargetComponent extends ObjectWidget implements OnDestroy, AfterViewInit {
private ngUnsubscribe: Subject = new Subject();
ids$: Observable;
- ids: string[];
+ idCount$: Observable;
search: FormControl = new FormControl(
'',
@@ -40,17 +40,22 @@ export class FilterTargetComponent extends ObjectWidget implements OnDestroy, Af
) {
super();
this.ids$ = this.store.select(fromFilters.getEntityCollection);
- this.ids$.subscribe(ids => {
- this.ids = [...ids];
- });
+
+ this.idCount$ = this.ids$.pipe(map(list => list.length));
this.search
.valueChanges
.pipe(
takeUntil(this.ngUnsubscribe),
- distinctUntilChanged()
+ distinctUntilChanged(),
+ withLatestFrom(this.ids$),
+ filter(([term, ids]) => term && term.length >= 4 && ids.indexOf(term) < 0),
+ map(([term]) => new QueryEntityIds({
+ term,
+ limit: 10
+ }))
)
- .subscribe(query => this.searchEntityIds(query));
+ .subscribe(action => this.store.dispatch(action));
this.script
.valueChanges
@@ -101,15 +106,6 @@ export class FilterTargetComponent extends ObjectWidget implements OnDestroy, Af
};
}
- searchEntityIds(term: string): void {
- if (term && term.length >= 4 && this.ids.indexOf(term) < 0) {
- this.store.dispatch(new QueryEntityIds({
- term,
- limit: 10
- }));
- }
- }
-
getButtonConfig(id: string): any {
let buttons = this.formProperty.getProperty('value').schema.buttons;
return (buttons || []).map(btn => ({
diff --git a/ui/src/app/shared/autocomplete/autocomplete.component.html b/ui/src/app/shared/autocomplete/autocomplete.component.html
index ff4fc99fd..7800382cd 100644
--- a/ui/src/app/shared/autocomplete/autocomplete.component.html
+++ b/ui/src/app/shared/autocomplete/autocomplete.component.html
@@ -6,32 +6,38 @@
[id]="fieldId"
class="form-control"
autocomplete="off"
+ autocorrect="off"
+ aria-autocomplete="list"
role="combobox"
type="text"
[placeholder]="placeholder"
[attr.aria-activedescendant]="activeDescendant"
- aria-autocomplete="both"
[attr.aria-owns]="fieldId + '__listbox'"
[attr.aria-expanded]="(menuIsVisible$ | async) ? 'true' : 'false'"
+ [attr.aria-haspopup]="fieldId + '__listbox'"
(focus)="handleInputFocus()"
(blur)="handleInputBlur()"
/>
-
\ No newline at end of file
+
+
diff --git a/ui/src/app/shared/autocomplete/autocomplete.component.ts b/ui/src/app/shared/autocomplete/autocomplete.component.ts
index a3d7d109a..cda5d6406 100644
--- a/ui/src/app/shared/autocomplete/autocomplete.component.ts
+++ b/ui/src/app/shared/autocomplete/autocomplete.component.ts
@@ -51,6 +51,7 @@ export class AutoCompleteComponent implements OnInit, OnDestroy, OnChanges, Afte
@Input() processing = false;
@Input() dropdown = false;
@Input() placeholder = '';
+ @Input() count = null;
@Output() more: EventEmitter = new EventEmitter();
@Output() onChange: EventEmitter = new EventEmitter();
@@ -118,14 +119,16 @@ export class AutoCompleteComponent implements OnInit, OnDestroy, OnChanges, Afte
}
ngOnChanges(changes: SimpleChanges): void {
- if (changes.matches && this.matches) {
+ if (changes.matches && this.matches && this.state.currentState.menuOpen) {
this.announceResults();
}
}
announceResults(): void {
const count = this.matches.length;
- this.live.announce(count === 0 ? 'No results available' : `${count} result${count === 1 ? '' : 's'} available`);
+ this.live.announce(count === 0 ?
+ `${this.noneFoundText}` :
+ `${count} result${count === 1 ? '' : 's'} available`, 'polite', 5000);
}
writeValue(value: any): void {
@@ -157,6 +160,12 @@ export class AutoCompleteComponent implements OnInit, OnDestroy, OnChanges, Afte
});
}
+ handleDropdown($event: MouseEvent | KeyboardEvent | Event): void {
+ const open = this.state.currentState.menuOpen;
+ this.state.setState({menuOpen: !open});
+ this.handleOptionFocus(0);
+ }
+
handleViewMore($event: MouseEvent | KeyboardEvent | Event): void {
$event.preventDefault();
$event.stopPropagation();
@@ -216,7 +225,6 @@ export class AutoCompleteComponent implements OnInit, OnDestroy, OnChanges, Afte
handleInputChange(query: string): void {
query = query || '';
-
const queryEmpty = query.length === 0;
const autoselect = this.hasAutoselect;
const optionsAvailable = this.matches.length > 0;
@@ -227,8 +235,6 @@ export class AutoCompleteComponent implements OnInit, OnDestroy, OnChanges, Afte
selected: searchForOptions ? ((autoselect && optionsAvailable) ? 0 : -1) : null
});
this.propagateChange(query);
-
- setTimeout(() => this.announceResults(), 250);
}
handleInputFocus(): void {
@@ -284,7 +290,6 @@ export class AutoCompleteComponent implements OnInit, OnDestroy, OnChanges, Afte
}
handleDownArrow(event: KeyboardEvent): void {
- event.preventDefault();
let isNotAtBottom = this.state.currentState.selected !== this.matches.length - 1;
if (this.showMoreAvailable) {
isNotAtBottom = this.state.currentState.selected !== this.matches.length;
@@ -293,6 +298,7 @@ export class AutoCompleteComponent implements OnInit, OnDestroy, OnChanges, Afte
if (allowMoveDown) {
this.handleOptionFocus(this.state.currentState.selected + 1);
}
+ event.preventDefault();
}
handleSpace(event: KeyboardEvent): void {
@@ -349,8 +355,8 @@ export class AutoCompleteComponent implements OnInit, OnDestroy, OnChanges, Afte
return !!(agent.match(/(iPod|iPhone|iPad)/g) && agent.match(/AppleWebKit/g));
}
- getOptionId(index): string {
- return `${this.fieldId}__option--${index}`;
+ getOptionId(index: string | number): string {
+ return `${this.fieldId}__option--${index}`.replace('/', '');
}
get hasAutoselect(): boolean {