mirror of
https://github.com/bpatrik/pigallery2.git
synced 2025-01-24 05:17:16 +02:00
Improving search modal UI including fixing search modal delete button event propagation #587
This commit is contained in:
parent
66d20e200f
commit
b9b41eded9
@ -8,7 +8,7 @@
|
|||||||
<hr>
|
<hr>
|
||||||
<app-gallery-search-query-entry
|
<app-gallery-search-query-entry
|
||||||
[(ngModel)]="searchQueryDTO"
|
[(ngModel)]="searchQueryDTO"
|
||||||
(change)="onQueryChange()"
|
(change)="onChange()"
|
||||||
(ngModelChange)="onChange()"
|
(ngModelChange)="onChange()"
|
||||||
name="search-root"
|
name="search-root"
|
||||||
(delete)="resetQuery()">
|
(delete)="resetQuery()">
|
||||||
|
@ -1,24 +1,7 @@
|
|||||||
import {
|
import {Component, EventEmitter, forwardRef, Input, Output,} from '@angular/core';
|
||||||
Component,
|
import {SearchQueryDTO, SearchQueryTypes, TextSearch,} from '../../../../../../common/entities/SearchQueryDTO';
|
||||||
EventEmitter,
|
import {ControlValueAccessor, NG_VALIDATORS, NG_VALUE_ACCESSOR, UntypedFormControl, ValidationErrors, Validator,} from '@angular/forms';
|
||||||
forwardRef,
|
import {SearchQueryParserService} from '../search-query-parser.service';
|
||||||
Input,
|
|
||||||
Output,
|
|
||||||
} from '@angular/core';
|
|
||||||
import {
|
|
||||||
SearchQueryDTO,
|
|
||||||
SearchQueryTypes,
|
|
||||||
TextSearch,
|
|
||||||
} from '../../../../../../common/entities/SearchQueryDTO';
|
|
||||||
import {
|
|
||||||
ControlValueAccessor,
|
|
||||||
UntypedFormControl,
|
|
||||||
NG_VALIDATORS,
|
|
||||||
NG_VALUE_ACCESSOR,
|
|
||||||
ValidationErrors,
|
|
||||||
Validator,
|
|
||||||
} from '@angular/forms';
|
|
||||||
import { SearchQueryParserService } from '../search-query-parser.service';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-gallery-search-query-builder',
|
selector: 'app-gallery-search-query-builder',
|
||||||
@ -38,8 +21,7 @@ import { SearchQueryParserService } from '../search-query-parser.service';
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class GallerySearchQueryBuilderComponent
|
export class GallerySearchQueryBuilderComponent
|
||||||
implements ControlValueAccessor, Validator
|
implements ControlValueAccessor, Validator {
|
||||||
{
|
|
||||||
public searchQueryDTO: SearchQueryDTO = {
|
public searchQueryDTO: SearchQueryDTO = {
|
||||||
type: SearchQueryTypes.any_text,
|
type: SearchQueryTypes.any_text,
|
||||||
text: '',
|
text: '',
|
||||||
@ -48,7 +30,8 @@ export class GallerySearchQueryBuilderComponent
|
|||||||
@Input() placeholder = $localize`Search`;
|
@Input() placeholder = $localize`Search`;
|
||||||
public rawSearchText = '';
|
public rawSearchText = '';
|
||||||
|
|
||||||
constructor(private searchQueryParserService: SearchQueryParserService) {}
|
constructor(private searchQueryParserService: SearchQueryParserService) {
|
||||||
|
}
|
||||||
|
|
||||||
validateRawSearchText(): void {
|
validateRawSearchText(): void {
|
||||||
try {
|
try {
|
||||||
@ -66,21 +49,16 @@ export class GallerySearchQueryBuilderComponent
|
|||||||
text: '',
|
text: '',
|
||||||
type: SearchQueryTypes.any_text,
|
type: SearchQueryTypes.any_text,
|
||||||
} as TextSearch;
|
} as TextSearch;
|
||||||
}
|
|
||||||
|
|
||||||
onQueryChange(): void {
|
|
||||||
this.rawSearchText = this.searchQueryParserService.stringify(
|
|
||||||
this.searchQueryDTO
|
|
||||||
);
|
|
||||||
this.onChange();
|
this.onChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
validate(control: UntypedFormControl): ValidationErrors {
|
validate(control: UntypedFormControl): ValidationErrors {
|
||||||
return { required: true };
|
return {required: true};
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
public onTouched(): void {}
|
public onTouched(): void {
|
||||||
|
}
|
||||||
|
|
||||||
public writeValue(obj: any): void {
|
public writeValue(obj: any): void {
|
||||||
this.searchQueryDTO = obj;
|
this.searchQueryDTO = obj;
|
||||||
@ -98,12 +76,17 @@ export class GallerySearchQueryBuilderComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
public onChange(): void {
|
public onChange(): void {
|
||||||
|
this.rawSearchText = this.searchQueryParserService.stringify(
|
||||||
|
this.searchQueryDTO
|
||||||
|
);
|
||||||
this.propagateChange(this.searchQueryDTO);
|
this.propagateChange(this.searchQueryDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
private propagateChange = (_: unknown): void => {};
|
private propagateChange = (_: unknown): void => {
|
||||||
|
};
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
private propagateTouch = (_: unknown): void => {};
|
private propagateTouch = (_: unknown): void => {
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -5,14 +5,3 @@
|
|||||||
label {
|
label {
|
||||||
margin-top: 0.3rem;
|
margin-top: 0.3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.match-type {
|
|
||||||
font-size: 2rem;
|
|
||||||
margin-top: -0.8rem;
|
|
||||||
font-weight: bold;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.match-type.exact-match {
|
|
||||||
color: #0069d9;
|
|
||||||
}
|
|
||||||
|
@ -51,24 +51,31 @@
|
|||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ng-container *ngIf="!IsListQuery">
|
<ng-container *ngIf="!IsListQuery">
|
||||||
<div class="col-md-3">
|
<div class="col-lg-4 col-xl-3">
|
||||||
<select
|
<div class="input-group">
|
||||||
id="searchType"
|
<select
|
||||||
name="searchType"
|
id="searchType"
|
||||||
class="form-select"
|
name="searchType"
|
||||||
[(ngModel)]="queryEntry.type"
|
class="form-select"
|
||||||
(ngModelChange)="onChangeType()">
|
[(ngModel)]="queryEntry.type"
|
||||||
<option *ngFor="let opt of SearchQueryTypesEnum" [ngValue]="opt.key">{{opt.key | stringifySearchType}}
|
(ngModelChange)="onChangeType()">
|
||||||
</option>
|
<option *ngFor="let opt of SearchQueryTypesEnum" [ngValue]="opt.key">{{opt.key | stringifySearchType}}
|
||||||
</select>
|
</option>
|
||||||
</div>
|
</select>
|
||||||
<div class="col-11 col-md-8" *ngIf="IsTextQuery">
|
<select
|
||||||
|
id="negate"
|
||||||
|
name="negate"
|
||||||
|
class="form-select w-auto flex-grow-0"
|
||||||
|
title="Negate"
|
||||||
|
p18n-title
|
||||||
|
[(ngModel)]="SelectedMatchType"
|
||||||
|
(ngModelChange)="onChange()">
|
||||||
|
<option *ngFor="let mt of MatchingTypes" [ngValue]="mt">{{mt}}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-10 col-lg" *ngIf="IsTextQuery">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span title="exact match"
|
|
||||||
p18n-title
|
|
||||||
class="match-type"
|
|
||||||
(click)="toggleMatchType()"
|
|
||||||
[class.exact-match]="AsTextQuery.matchType === TextSearchQueryMatchTypes.exact_match">"</span>
|
|
||||||
<input
|
<input
|
||||||
id="searchField"
|
id="searchField"
|
||||||
name="searchField"
|
name="searchField"
|
||||||
@ -79,15 +86,10 @@
|
|||||||
(change)="onChange()"
|
(change)="onChange()"
|
||||||
(ngModelChange)="onChange()"
|
(ngModelChange)="onChange()"
|
||||||
type="text"/>
|
type="text"/>
|
||||||
<span title="exact match"
|
|
||||||
p18n-title
|
|
||||||
class="match-type"
|
|
||||||
(click)="toggleMatchType()"
|
|
||||||
[class.exact-match]="AsTextQuery.matchType === TextSearchQueryMatchTypes.exact_match">"</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ng-container [ngSwitch]="queryEntry.type">
|
<ng-container [ngSwitch]="queryEntry.type">
|
||||||
<div *ngSwitchCase="SearchQueryTypes.distance" class="col-11 col-md-8 ">
|
<div *ngSwitchCase="SearchQueryTypes.distance" class="col-10 col-lg">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
@ -118,7 +120,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Range Search Query -->
|
<!-- Range Search Query -->
|
||||||
<div *ngSwitchCase="SearchQueryTypes.from_date" class="col-11 col-md-8 d-flex">
|
<div *ngSwitchCase="SearchQueryTypes.from_date" class="col-10 col-lg d-flex">
|
||||||
<input id="from_date"
|
<input id="from_date"
|
||||||
name="from_date"
|
name="from_date"
|
||||||
title="From date"
|
title="From date"
|
||||||
@ -129,7 +131,7 @@
|
|||||||
class="form-control input-md rounded-2"
|
class="form-control input-md rounded-2"
|
||||||
type="date">
|
type="date">
|
||||||
</div>
|
</div>
|
||||||
<div *ngSwitchCase="SearchQueryTypes.to_date" class="col-11 col-md-8 d-flex">
|
<div *ngSwitchCase="SearchQueryTypes.to_date" class="col-10 col-lg d-flex">
|
||||||
<input id="to_date"
|
<input id="to_date"
|
||||||
name="to_date"
|
name="to_date"
|
||||||
title="To date"
|
title="To date"
|
||||||
@ -140,7 +142,7 @@
|
|||||||
class="form-control input-md rounded-2"
|
class="form-control input-md rounded-2"
|
||||||
type="date">
|
type="date">
|
||||||
</div>
|
</div>
|
||||||
<div *ngSwitchCase="SearchQueryTypes.min_rating" class="col-11 col-md-8 d-flex">
|
<div *ngSwitchCase="SearchQueryTypes.min_rating" class="col-10 col-lg d-flex">
|
||||||
<input id="minRating"
|
<input id="minRating"
|
||||||
name="minRating"
|
name="minRating"
|
||||||
title="Minimum Rating"
|
title="Minimum Rating"
|
||||||
@ -153,7 +155,7 @@
|
|||||||
(ngModelChange)="onChange()"
|
(ngModelChange)="onChange()"
|
||||||
type="number">
|
type="number">
|
||||||
</div>
|
</div>
|
||||||
<div *ngSwitchCase="SearchQueryTypes.max_rating" class="col-11 col-md-8 d-flex">
|
<div *ngSwitchCase="SearchQueryTypes.max_rating" class="col-10 col-lg d-flex">
|
||||||
<input id="maxRating"
|
<input id="maxRating"
|
||||||
name="maxRating"
|
name="maxRating"
|
||||||
title="Maximum Rating"
|
title="Maximum Rating"
|
||||||
@ -166,7 +168,7 @@
|
|||||||
(ngModelChange)="onChange()"
|
(ngModelChange)="onChange()"
|
||||||
type="number">
|
type="number">
|
||||||
</div>
|
</div>
|
||||||
<div *ngSwitchCase="SearchQueryTypes.min_resolution" class="col-11 col-md-8">
|
<div *ngSwitchCase="SearchQueryTypes.min_resolution" class="col-10 col-lg">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input id="minResolution"
|
<input id="minResolution"
|
||||||
name="minResolution"
|
name="minResolution"
|
||||||
@ -182,7 +184,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngSwitchCase="SearchQueryTypes.max_resolution" class="col-11 col-md-8">
|
<div *ngSwitchCase="SearchQueryTypes.max_resolution" class="col-10 col-lg">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input id="maxResolution"
|
<input id="maxResolution"
|
||||||
name="maxResolution"
|
name="maxResolution"
|
||||||
@ -197,7 +199,7 @@
|
|||||||
<span class="input-group-text">Mpx</span>
|
<span class="input-group-text">Mpx</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div *ngSwitchCase="SearchQueryTypes.orientation" class="col-11 col-md-8 d-flex">
|
<div *ngSwitchCase="SearchQueryTypes.orientation" class="col-10 col-lg d-flex">
|
||||||
<div class="input-group col-md-6">
|
<div class="input-group col-md-6">
|
||||||
<select class="form-select rounded-2"
|
<select class="form-select rounded-2"
|
||||||
[(ngModel)]="AsOrientationQuery.landscape"
|
[(ngModel)]="AsOrientationQuery.landscape"
|
||||||
@ -212,11 +214,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<button [ngClass]="'btn-danger'"
|
<div class="col-2 col-lg-1 align-self-center">
|
||||||
class="btn float-end col-1 align-self-center"
|
<button [ngClass]="'btn-danger'"
|
||||||
(click)="deleteItem()">
|
class="btn w-auto float-end"
|
||||||
<span class="oi oi-trash" aria-hidden="true" aria-label="Delete"></span>
|
(click)="deleteItem()">
|
||||||
</button>
|
<span class="oi oi-trash" aria-hidden="true" aria-label="Delete"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, EventEmitter, forwardRef, Output } from '@angular/core';
|
import {Component, EventEmitter, forwardRef, Output} from '@angular/core';
|
||||||
import {
|
import {
|
||||||
DistanceSearch,
|
DistanceSearch,
|
||||||
ListSearchQueryTypes,
|
ListSearchQueryTypes,
|
||||||
@ -12,15 +12,8 @@ import {
|
|||||||
TextSearchQueryMatchTypes,
|
TextSearchQueryMatchTypes,
|
||||||
TextSearchQueryTypes,
|
TextSearchQueryTypes,
|
||||||
} from '../../../../../../common/entities/SearchQueryDTO';
|
} from '../../../../../../common/entities/SearchQueryDTO';
|
||||||
import { Utils } from '../../../../../../common/Utils';
|
import {Utils} from '../../../../../../common/Utils';
|
||||||
import {
|
import {ControlValueAccessor, NG_VALIDATORS, NG_VALUE_ACCESSOR, UntypedFormControl, ValidationErrors, Validator,} from '@angular/forms';
|
||||||
ControlValueAccessor,
|
|
||||||
UntypedFormControl,
|
|
||||||
NG_VALIDATORS,
|
|
||||||
NG_VALUE_ACCESSOR,
|
|
||||||
ValidationErrors,
|
|
||||||
Validator,
|
|
||||||
} from '@angular/forms';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-gallery-search-query-entry',
|
selector: 'app-gallery-search-query-entry',
|
||||||
@ -40,8 +33,7 @@ import {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class GallerySearchQueryEntryComponent
|
export class GallerySearchQueryEntryComponent
|
||||||
implements ControlValueAccessor, Validator
|
implements ControlValueAccessor, Validator {
|
||||||
{
|
|
||||||
public queryEntry: SearchQueryDTO;
|
public queryEntry: SearchQueryDTO;
|
||||||
public SearchQueryTypesEnum: { value: string; key: SearchQueryTypes }[];
|
public SearchQueryTypesEnum: { value: string; key: SearchQueryTypes }[];
|
||||||
public SearchQueryTypes = SearchQueryTypes;
|
public SearchQueryTypes = SearchQueryTypes;
|
||||||
@ -93,7 +85,48 @@ export class GallerySearchQueryEntryComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
validate(control: UntypedFormControl): ValidationErrors {
|
validate(control: UntypedFormControl): ValidationErrors {
|
||||||
return { required: true };
|
return {required: true};
|
||||||
|
}
|
||||||
|
|
||||||
|
get MatchingTypes(): string[] {
|
||||||
|
if (this.IsListQuery) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
if (this.IsTextQuery) {
|
||||||
|
return ['=~', '=', '!=', '!~'];
|
||||||
|
}
|
||||||
|
return ['=', '!=']; //normal negatable query
|
||||||
|
}
|
||||||
|
|
||||||
|
get SelectedMatchType(): string {
|
||||||
|
if (this.AsTextQuery.matchType !== TextSearchQueryMatchTypes.like) {
|
||||||
|
if (this.AsTextQuery.negate) {
|
||||||
|
return '!=';
|
||||||
|
} else {
|
||||||
|
return '=';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.AsTextQuery.negate) {
|
||||||
|
return '!~';
|
||||||
|
} else {
|
||||||
|
return '=~';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set SelectedMatchType(value: string) {
|
||||||
|
if (this.IsListQuery) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.AsTextQuery.negate = value.charAt(0) === '!';
|
||||||
|
if (!this.IsTextQuery) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (value === '!~' || value === '=~') {
|
||||||
|
this.AsTextQuery.matchType = TextSearchQueryMatchTypes.like;
|
||||||
|
} else {
|
||||||
|
this.AsTextQuery.matchType = TextSearchQueryMatchTypes.exact_match;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addQuery(): void {
|
addQuery(): void {
|
||||||
@ -110,14 +143,14 @@ export class GallerySearchQueryEntryComponent
|
|||||||
if (this.IsListQuery) {
|
if (this.IsListQuery) {
|
||||||
delete this.AsTextQuery.text;
|
delete this.AsTextQuery.text;
|
||||||
this.AsListQuery.list = this.AsListQuery.list || [
|
this.AsListQuery.list = this.AsListQuery.list || [
|
||||||
{ type: SearchQueryTypes.any_text, text: '' } as TextSearch,
|
{type: SearchQueryTypes.any_text, text: ''} as TextSearch,
|
||||||
{ type: SearchQueryTypes.any_text, text: '' } as TextSearch,
|
{type: SearchQueryTypes.any_text, text: ''} as TextSearch,
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
delete this.AsListQuery.list;
|
delete this.AsListQuery.list;
|
||||||
}
|
}
|
||||||
if (this.queryEntry.type === SearchQueryTypes.distance) {
|
if (this.queryEntry.type === SearchQueryTypes.distance) {
|
||||||
this.AsDistanceQuery.from = { text: '' };
|
this.AsDistanceQuery.from = {text: ''};
|
||||||
this.AsDistanceQuery.distance = 1;
|
this.AsDistanceQuery.distance = 1;
|
||||||
} else {
|
} else {
|
||||||
delete this.AsDistanceQuery.from;
|
delete this.AsDistanceQuery.from;
|
||||||
@ -136,12 +169,14 @@ export class GallerySearchQueryEntryComponent
|
|||||||
this.delete.emit();
|
this.delete.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
itemDeleted(i: number): void {
|
itemDeleted(index: number): void {
|
||||||
this.AsListQuery.list.splice(i, 1);
|
this.AsListQuery.list.splice(index, 1);
|
||||||
|
this.onChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
public onTouched(): void {}
|
public onTouched(): void {
|
||||||
|
}
|
||||||
|
|
||||||
public writeValue(obj: SearchQueryDTO): void {
|
public writeValue(obj: SearchQueryDTO): void {
|
||||||
this.queryEntry = obj;
|
this.queryEntry = obj;
|
||||||
@ -159,18 +194,13 @@ export class GallerySearchQueryEntryComponent
|
|||||||
this.propagateChange(this.queryEntry);
|
this.propagateChange(this.queryEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
public toggleMatchType(): void {
|
|
||||||
this.AsTextQuery.matchType =
|
|
||||||
this.AsTextQuery.matchType === TextSearchQueryMatchTypes.exact_match
|
|
||||||
? TextSearchQueryMatchTypes.like
|
|
||||||
: TextSearchQueryMatchTypes.exact_match;
|
|
||||||
this.onChange();
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
private propagateChange = (_: unknown): void => {};
|
private propagateChange = (_: unknown): void => {
|
||||||
|
};
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
private propagateTouch = (_: unknown): void => {};
|
private propagateTouch = (_: unknown): void => {
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user