remove focus, use custom highlight

This commit is contained in:
Dan Percic 2021-07-27 18:00:58 +03:00
parent a65daaf90c
commit e50e5cbfb2
5 changed files with 67 additions and 91 deletions

View File

@ -1,6 +1,6 @@
export interface SpotlightSearchAction { export interface SpotlightSearchAction {
text: string; readonly text: string;
action: (query: string) => void; readonly action: (query: string) => void;
icon?: string; readonly icon?: string;
hide?: boolean; readonly hide?: boolean;
} }

View File

@ -1,6 +1,6 @@
import { SpotlightSearchAction } from './spotlight-search-action'; import { SpotlightSearchAction } from './spotlight-search-action';
export interface SpotlightSearchDialogData { export interface SpotlightSearchDialogData {
actionsConfig: SpotlightSearchAction[]; readonly actionsConfig: SpotlightSearchAction[];
placeholder: string; readonly placeholder: string;
} }

View File

@ -1,43 +1,30 @@
<div class="spotlight-wrapper"> <form class="spotlight-wrapper" [formGroup]="formGroup">
<form [formGroup]="formGroup"> <div class="search d-flex">
<div class="search d-flex"> <input id="query" type="text" formControlName="query" autocomplete="off" class="spotlight-row" [placeholder]="data.placeholder" />
<input
tabindex="-1"
id="query"
type="text"
formControlName="query"
autocomplete="off"
class="spotlight-row"
[placeholder]="data.placeholder"
/>
<mat-icon class="mr-34" *ngIf="(showActions$ | async) === false" [svgIcon]="'red:search'"></mat-icon> <ng-container *ngIf="showActions$ | async as showActions">
<mat-icon class="mr-34" *ngIf="!showActions" [svgIcon]="'red:search'"></mat-icon>
<redaction-circle-button <redaction-circle-button
*ngIf="showActions$ | async" *ngIf="showActions"
class="mr-24" class="mr-24"
(action)="close()" (action)="dialogRef.close()"
icon="red:close" icon="red:close"
></redaction-circle-button> ></redaction-circle-button>
</div>
<div class="divider"></div>
<ng-container *ngIf="showActions$ | async">
<ng-container *ngFor="let item of data.actionsConfig">
<button
*ngIf="!item.hide"
#actions
(keydown.space)="$event.preventDefault()"
(keyup.space)="$event.preventDefault()"
tabindex="0"
class="spotlight-row focus pointer"
(click)="item.action(formGroup.get('query').value); close()"
>
<mat-icon class="mr-16" [svgIcon]="item.icon"></mat-icon>
<span>{{ item.text }}</span>
</button>
</ng-container>
</ng-container> </ng-container>
</form> </div>
</div>
<div class="divider"></div>
<ng-container *ngIf="showActions$ | async">
<button
*ngFor="let item of shownActions; let index = index"
class="spotlight-row pointer"
[class.highlight]="(currentActionIdx$ | async) === index"
(click)="item.action(formGroup.get('query').value); dialogRef.close()"
>
<mat-icon class="mr-16" [svgIcon]="item.icon"></mat-icon>
<span>{{ item.text }}</span>
</button>
</ng-container>
</form>

View File

@ -2,8 +2,6 @@
.spotlight-wrapper { .spotlight-wrapper {
overflow: hidden; overflow: hidden;
width: 750px;
margin: auto;
position: absolute; position: absolute;
top: 15%; top: 15%;
left: 0; left: 0;
@ -13,10 +11,9 @@
} }
.spotlight-row { .spotlight-row {
display: block; display: flex;
width: 750px; align-items: center;
height: 60px; height: 60px;
margin: auto;
text-align: left; text-align: left;
font-size: 16px; font-size: 16px;
font-weight: 500; font-weight: 500;
@ -27,7 +24,13 @@
background-color: $white; background-color: $white;
} }
.focus:focus { .spotlight-row,
.spotlight-wrapper {
width: 750px;
margin: auto;
}
.highlight {
background-color: $grey-2; background-color: $grey-2;
} }
@ -41,10 +44,6 @@
background-color: rgba(226, 228, 233, 0.9); background-color: rgba(226, 228, 233, 0.9);
} }
input {
width: 668px !important;
}
mat-icon { mat-icon {
width: 14px; width: 14px;
height: 14px; height: 14px;

View File

@ -1,9 +1,9 @@
import { ChangeDetectionStrategy, Component, ElementRef, HostListener, Inject, QueryList, ViewChildren } from '@angular/core'; import { ChangeDetectionStrategy, Component, HostListener, Inject } from '@angular/core';
import { FormBuilder } from '@angular/forms'; import { FormBuilder } from '@angular/forms';
import { debounceTime, map, startWith, tap } from 'rxjs/operators'; import { debounceTime, distinctUntilChanged, map, startWith } from 'rxjs/operators';
import { debounce } from '@utils/debounce';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { SpotlightSearchDialogData } from '@components/spotlight-search/spotlight-search-dialog-data'; import { SpotlightSearchDialogData } from '@components/spotlight-search/spotlight-search-dialog-data';
import { BehaviorSubject } from 'rxjs';
@Component({ @Component({
selector: 'redaction-spotlight-search', selector: 'redaction-spotlight-search',
@ -12,58 +12,48 @@ import { SpotlightSearchDialogData } from '@components/spotlight-search/spotligh
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class SpotlightSearchComponent { export class SpotlightSearchComponent {
@ViewChildren('actions') private readonly _currentActionIdx$ = new BehaviorSubject(0);
private readonly _actions: QueryList<ElementRef>;
private _currentActionIdx = 0;
formGroup = this._formBuilder.group({ query: [''] }); readonly currentActionIdx$ = this._currentActionIdx$.asObservable().pipe(distinctUntilChanged());
query$ = this.formGroup.get('query').valueChanges.pipe(startWith('')); readonly shownActions = this.data.actionsConfig.filter(a => !a.hide);
showActions$ = this.query$.pipe( readonly formGroup = this._formBuilder.group({ query: [''] });
readonly showActions$ = this.formGroup.get('query').valueChanges.pipe(
startWith(''),
debounceTime(300), debounceTime(300),
tap(value => this._restoreFocusOnAction(value === '')),
map(value => value !== '') map(value => value !== '')
); );
constructor( constructor(
private readonly _formBuilder: FormBuilder, private readonly _formBuilder: FormBuilder,
private readonly _dialogRef: MatDialogRef<SpotlightSearchComponent>, readonly dialogRef: MatDialogRef<SpotlightSearchComponent>,
@Inject(MAT_DIALOG_DATA) @Inject(MAT_DIALOG_DATA)
readonly data: SpotlightSearchDialogData readonly data: SpotlightSearchDialogData
) {} ) {}
close() { @HostListener('document:keydown', ['$event'])
this._dialogRef.close(); handleKeyDown(event: KeyboardEvent): void {
if (['ArrowDown', 'ArrowUp'].includes(event.code)) {
event.preventDefault();
return event.stopPropagation();
}
} }
@HostListener('document:keyup', ['$event']) @HostListener('document:keyup', ['$event'])
handleKeyDown(event: KeyboardEvent) { handleKeyUp(event: KeyboardEvent): void {
if (event.code === 'ArrowDown' && this._actions) { if (['ArrowDown', 'ArrowUp'].includes(event.code)) {
this._currentActionIdx++; event.preventDefault();
return this._restoreFocusOnAction(this._currentActionIdx === this._actions.length); event.stopPropagation();
const index = this._currentActionIdx + event.code === 'ArrowDown' ? 1 : -1;
return this._currentActionIdx$.next(this._fixIndexOutOfBound(index));
} }
if (event.code === 'ArrowUp' && this._actions) {
if (this._currentActionIdx === 0) this._currentActionIdx = this._actions.length - 1;
else this._currentActionIdx--;
return this._restoreFocusOnAction();
}
if (['Tab'].includes(event.code)) {
return;
}
if (this._actions.find((_, index) => index === this._currentActionIdx)?.nativeElement === document.activeElement) {
let query = this.formGroup.get('query').value as string;
if (event.code === 'Backspace') query = query.substring(0, query.length - 1);
else if (event.key.length === 1) query = query + event.key;
this.formGroup.patchValue({ query: query });
}
document.getElementById('query').focus();
} }
@debounce(50) private get _currentActionIdx(): number {
private _restoreFocusOnAction(resetToFirst = false) { return this._currentActionIdx$.getValue();
if (resetToFirst) this._currentActionIdx = 0; }
this._actions.find((_, index) => index === this._currentActionIdx)?.nativeElement.focus();
private _fixIndexOutOfBound(index: number): number {
const indexOutOfBound = index < 0 || index >= this.shownActions.length;
return indexOutOfBound ? Math.abs(this.shownActions.length - Math.abs(index)) : index;
} }
} }