remove focus, use custom highlight
This commit is contained in:
parent
a65daaf90c
commit
e50e5cbfb2
@ -1,6 +1,6 @@
|
||||
export interface SpotlightSearchAction {
|
||||
text: string;
|
||||
action: (query: string) => void;
|
||||
icon?: string;
|
||||
hide?: boolean;
|
||||
readonly text: string;
|
||||
readonly action: (query: string) => void;
|
||||
readonly icon?: string;
|
||||
readonly hide?: boolean;
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { SpotlightSearchAction } from './spotlight-search-action';
|
||||
|
||||
export interface SpotlightSearchDialogData {
|
||||
actionsConfig: SpotlightSearchAction[];
|
||||
placeholder: string;
|
||||
readonly actionsConfig: SpotlightSearchAction[];
|
||||
readonly placeholder: string;
|
||||
}
|
||||
|
||||
@ -1,43 +1,30 @@
|
||||
<div class="spotlight-wrapper">
|
||||
<form [formGroup]="formGroup">
|
||||
<div class="search d-flex">
|
||||
<input
|
||||
tabindex="-1"
|
||||
id="query"
|
||||
type="text"
|
||||
formControlName="query"
|
||||
autocomplete="off"
|
||||
class="spotlight-row"
|
||||
[placeholder]="data.placeholder"
|
||||
/>
|
||||
<form class="spotlight-wrapper" [formGroup]="formGroup">
|
||||
<div class="search d-flex">
|
||||
<input 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
|
||||
*ngIf="showActions$ | async"
|
||||
*ngIf="showActions"
|
||||
class="mr-24"
|
||||
(action)="close()"
|
||||
(action)="dialogRef.close()"
|
||||
icon="red:close"
|
||||
></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>
|
||||
</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>
|
||||
|
||||
@ -2,8 +2,6 @@
|
||||
|
||||
.spotlight-wrapper {
|
||||
overflow: hidden;
|
||||
width: 750px;
|
||||
margin: auto;
|
||||
position: absolute;
|
||||
top: 15%;
|
||||
left: 0;
|
||||
@ -13,10 +11,9 @@
|
||||
}
|
||||
|
||||
.spotlight-row {
|
||||
display: block;
|
||||
width: 750px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 60px;
|
||||
margin: auto;
|
||||
text-align: left;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
@ -27,7 +24,13 @@
|
||||
background-color: $white;
|
||||
}
|
||||
|
||||
.focus:focus {
|
||||
.spotlight-row,
|
||||
.spotlight-wrapper {
|
||||
width: 750px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
background-color: $grey-2;
|
||||
}
|
||||
|
||||
@ -41,10 +44,6 @@
|
||||
background-color: rgba(226, 228, 233, 0.9);
|
||||
}
|
||||
|
||||
input {
|
||||
width: 668px !important;
|
||||
}
|
||||
|
||||
mat-icon {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
|
||||
@ -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 { debounceTime, map, startWith, tap } from 'rxjs/operators';
|
||||
import { debounce } from '@utils/debounce';
|
||||
import { debounceTime, distinctUntilChanged, map, startWith } from 'rxjs/operators';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { SpotlightSearchDialogData } from '@components/spotlight-search/spotlight-search-dialog-data';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-spotlight-search',
|
||||
@ -12,58 +12,48 @@ import { SpotlightSearchDialogData } from '@components/spotlight-search/spotligh
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class SpotlightSearchComponent {
|
||||
@ViewChildren('actions')
|
||||
private readonly _actions: QueryList<ElementRef>;
|
||||
private _currentActionIdx = 0;
|
||||
private readonly _currentActionIdx$ = new BehaviorSubject(0);
|
||||
|
||||
formGroup = this._formBuilder.group({ query: [''] });
|
||||
query$ = this.formGroup.get('query').valueChanges.pipe(startWith(''));
|
||||
showActions$ = this.query$.pipe(
|
||||
readonly currentActionIdx$ = this._currentActionIdx$.asObservable().pipe(distinctUntilChanged());
|
||||
readonly shownActions = this.data.actionsConfig.filter(a => !a.hide);
|
||||
readonly formGroup = this._formBuilder.group({ query: [''] });
|
||||
readonly showActions$ = this.formGroup.get('query').valueChanges.pipe(
|
||||
startWith(''),
|
||||
debounceTime(300),
|
||||
tap(value => this._restoreFocusOnAction(value === '')),
|
||||
map(value => value !== '')
|
||||
);
|
||||
|
||||
constructor(
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
private readonly _dialogRef: MatDialogRef<SpotlightSearchComponent>,
|
||||
readonly dialogRef: MatDialogRef<SpotlightSearchComponent>,
|
||||
@Inject(MAT_DIALOG_DATA)
|
||||
readonly data: SpotlightSearchDialogData
|
||||
) {}
|
||||
|
||||
close() {
|
||||
this._dialogRef.close();
|
||||
@HostListener('document:keydown', ['$event'])
|
||||
handleKeyDown(event: KeyboardEvent): void {
|
||||
if (['ArrowDown', 'ArrowUp'].includes(event.code)) {
|
||||
event.preventDefault();
|
||||
return event.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('document:keyup', ['$event'])
|
||||
handleKeyDown(event: KeyboardEvent) {
|
||||
if (event.code === 'ArrowDown' && this._actions) {
|
||||
this._currentActionIdx++;
|
||||
return this._restoreFocusOnAction(this._currentActionIdx === this._actions.length);
|
||||
handleKeyUp(event: KeyboardEvent): void {
|
||||
if (['ArrowDown', 'ArrowUp'].includes(event.code)) {
|
||||
event.preventDefault();
|
||||
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 _restoreFocusOnAction(resetToFirst = false) {
|
||||
if (resetToFirst) this._currentActionIdx = 0;
|
||||
this._actions.find((_, index) => index === this._currentActionIdx)?.nativeElement.focus();
|
||||
private get _currentActionIdx(): number {
|
||||
return this._currentActionIdx$.getValue();
|
||||
}
|
||||
|
||||
private _fixIndexOutOfBound(index: number): number {
|
||||
const indexOutOfBound = index < 0 || index >= this.shownActions.length;
|
||||
return indexOutOfBound ? Math.abs(this.shownActions.length - Math.abs(index)) : index;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user