remove focus, use custom highlight
This commit is contained in:
parent
a65daaf90c
commit
e50e5cbfb2
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user