Fixed <select> flickering
This commit is contained in:
parent
a055e88063
commit
6129257a3b
@ -5,17 +5,24 @@ import {HttpClient} from "@angular/common/http";
|
|||||||
import {environment} from "../../environments/environment";
|
import {environment} from "../../environments/environment";
|
||||||
import {countryCodeToFlag, formatDuration} from "../format";
|
import {countryCodeToFlag, formatDuration} from "../format";
|
||||||
import {OsuGradeComponent} from "../../corelib/components/osu-grade/osu-grade.component";
|
import {OsuGradeComponent} from "../../corelib/components/osu-grade/osu-grade.component";
|
||||||
import {Field, Query, QueryBuilderComponent} from "../../corelib/components/query-builder/query-builder.component";
|
import {
|
||||||
|
FieldType,
|
||||||
|
Operator,
|
||||||
|
Query,
|
||||||
|
QueryBuilderComponent
|
||||||
|
} from "../../corelib/components/query-builder/query-builder.component";
|
||||||
import {RouterLink} from "@angular/router";
|
import {RouterLink} from "@angular/router";
|
||||||
import {CalculatePageRangePipe} from "../../corelib/calculate-page-range.pipe";
|
import {CalculatePageRangePipe} from "../../corelib/calculate-page-range.pipe";
|
||||||
import {DownloadFilesService} from "../../corelib/service/download-files.service";
|
import {DownloadFilesService} from "../../corelib/service/download-files.service";
|
||||||
import {CuteLoadingComponent} from "../../corelib/components/cute-loading/cute-loading.component";
|
import {CuteLoadingComponent} from "../../corelib/components/cute-loading/cute-loading.component";
|
||||||
|
import {Title} from "@angular/platform-browser";
|
||||||
|
|
||||||
export interface SchemaField {
|
export interface SchemaField {
|
||||||
name: string;
|
name: string;
|
||||||
shortName: string;
|
shortName: string;
|
||||||
category: 'user' | 'score' | 'beatmap' | 'metrics';
|
category: 'user' | 'score' | 'beatmap' | 'metrics';
|
||||||
type: 'number' | 'string' | 'flag' | 'grade' | 'boolean' | 'datetime' | 'playtime';
|
type: 'number' | 'string' | 'flag' | 'grade' | 'boolean' | 'datetime' | 'playtime';
|
||||||
|
validOperators: Operator[];
|
||||||
active: boolean;
|
active: boolean;
|
||||||
description: string;
|
description: string;
|
||||||
}
|
}
|
||||||
@ -63,6 +70,7 @@ interface Sorting {
|
|||||||
export class SearchComponent implements OnInit {
|
export class SearchComponent implements OnInit {
|
||||||
|
|
||||||
constructor(private httpClient: HttpClient,
|
constructor(private httpClient: HttpClient,
|
||||||
|
private title: Title,
|
||||||
public downloadFilesService: DownloadFilesService) { }
|
public downloadFilesService: DownloadFilesService) { }
|
||||||
|
|
||||||
isError = false;
|
isError = false;
|
||||||
@ -76,10 +84,14 @@ export class SearchComponent implements OnInit {
|
|||||||
queries: Query[] | null = null;
|
queries: Query[] | null = null;
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
this.title.setTitle("/k/ - advanced search");
|
||||||
this.isLoadingSchema = true;
|
this.isLoadingSchema = true;
|
||||||
this.httpClient.get<SchemaResponse>(`${environment.apiUrl}/search/schema`,).subscribe({
|
this.httpClient.get<SchemaResponse>(`${environment.apiUrl}/search/schema`,).subscribe({
|
||||||
next: (response) => {
|
next: (response) => {
|
||||||
this.fields = response.fields;
|
this.fields = response.fields;
|
||||||
|
this.fields.forEach(field => {
|
||||||
|
field.validOperators = this.getOperators(field.type);
|
||||||
|
})
|
||||||
this.loadPreviousFromLocalStorage();
|
this.loadPreviousFromLocalStorage();
|
||||||
this.isLoadingSchema = false;
|
this.isLoadingSchema = false;
|
||||||
},
|
},
|
||||||
@ -89,6 +101,34 @@ export class SearchComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getOperators(fieldType: FieldType | undefined): Operator[] {
|
||||||
|
switch (fieldType) {
|
||||||
|
case 'number':
|
||||||
|
return ['=', '>', '<', '>=', '<=', '!=']
|
||||||
|
.map((operatorType: String) => ({operatorType: operatorType, acceptsValues: 'any'}) as Operator);
|
||||||
|
case 'string':
|
||||||
|
return ['=', 'contains', 'like']
|
||||||
|
.map((operatorType: String) => ({operatorType: operatorType, acceptsValues: 'any'}) as Operator);
|
||||||
|
case 'boolean':
|
||||||
|
return ['=', '!=']
|
||||||
|
.map((operatorType: String) => ({operatorType: operatorType, acceptsValues: 'boolean'}) as Operator);
|
||||||
|
case 'flag':
|
||||||
|
return ['=', '!=']
|
||||||
|
.map((operatorType: String) => ({operatorType: operatorType, acceptsValues: 'flag'}) as Operator);
|
||||||
|
case 'grade':
|
||||||
|
return ['=', '!=']
|
||||||
|
.map((operatorType: String) => ({operatorType: operatorType, acceptsValues: 'grade'}) as Operator);
|
||||||
|
case 'datetime':
|
||||||
|
return ['before', 'after']
|
||||||
|
.map((operatorType: String) => ({operatorType: operatorType, acceptsValues: 'datetime'}) as Operator);
|
||||||
|
case 'playtime':
|
||||||
|
return ['>', '<', '>=', '<=']
|
||||||
|
.map((operatorType: String) => ({operatorType: operatorType, acceptsValues: 'any'}) as Operator);
|
||||||
|
default:
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private loadPreviousFromLocalStorage() {
|
private loadPreviousFromLocalStorage() {
|
||||||
const storedQueries = localStorage.getItem('search_settings');
|
const storedQueries = localStorage.getItem('search_settings');
|
||||||
if (storedQueries) {
|
if (storedQueries) {
|
||||||
@ -227,6 +267,6 @@ export class SearchComponent implements OnInit {
|
|||||||
|
|
||||||
protected readonly countryCodeToFlag = countryCodeToFlag;
|
protected readonly countryCodeToFlag = countryCodeToFlag;
|
||||||
protected readonly Math = Math;
|
protected readonly Math = Math;
|
||||||
|
|
||||||
protected readonly formatDuration = formatDuration;
|
protected readonly formatDuration = formatDuration;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,13 +8,8 @@ export type FieldType = 'number' | 'string' | 'flag' | 'grade' | 'boolean' | 'da
|
|||||||
export type OperatorType = '=' | '>' | '<' | 'contains' | 'like' | '>=' | '<=' | '!=';
|
export type OperatorType = '=' | '>' | '<' | 'contains' | 'like' | '>=' | '<=' | '!=';
|
||||||
export type ValueType = 'any' | 'boolean' | 'flag' | 'grade' | 'datetime';
|
export type ValueType = 'any' | 'boolean' | 'flag' | 'grade' | 'datetime';
|
||||||
|
|
||||||
export interface Field {
|
|
||||||
name: string;
|
|
||||||
type: FieldType;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Predicate {
|
export interface Predicate {
|
||||||
field: Field | null;
|
field: SchemaField | null;
|
||||||
operator: Operator | null;
|
operator: Operator | null;
|
||||||
value: string | number | null;
|
value: string | number | null;
|
||||||
}
|
}
|
||||||
@ -27,7 +22,7 @@ export interface Operator {
|
|||||||
export interface Query {
|
export interface Query {
|
||||||
predicates: Predicate[];
|
predicates: Predicate[];
|
||||||
logicalOperator: 'AND' | 'OR';
|
logicalOperator: 'AND' | 'OR';
|
||||||
childQueries?: Query[]; // Optional property for sub-queries
|
childQueries?: Query[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
|||||||
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<select [disabled]="!predicate.field">
|
<select [disabled]="!predicate.field">
|
||||||
<option *ngFor="let operator of getOperators(predicate.field?.type)"
|
<option *ngFor="let operator of predicate.field?.validOperators"
|
||||||
[selected]="operator.operatorType === predicate.operator?.operatorType"
|
[selected]="operator.operatorType === predicate.operator?.operatorType"
|
||||||
(click)="predicate.operator = operator; this.queryChanged.emit()">
|
(click)="predicate.operator = operator; this.queryChanged.emit()">
|
||||||
{{ operator.operatorType }}
|
{{ operator.operatorType }}
|
||||||
|
|||||||
@ -32,35 +32,7 @@ export class QueryComponent {
|
|||||||
|
|
||||||
onFieldChange(predicate: Predicate, selectedField: any): void {
|
onFieldChange(predicate: Predicate, selectedField: any): void {
|
||||||
predicate.field = selectedField;
|
predicate.field = selectedField;
|
||||||
predicate.operator = this.getOperators(selectedField.type)[0];
|
predicate.operator = selectedField.validOperators[0];
|
||||||
}
|
|
||||||
|
|
||||||
getOperators(fieldType: FieldType | undefined): Operator[] {
|
|
||||||
switch (fieldType) {
|
|
||||||
case 'number':
|
|
||||||
return ['=', '>', '<', '>=', '<=', '!=']
|
|
||||||
.map((operatorType: String) => ({operatorType: operatorType, acceptsValues: 'any'}) as Operator);
|
|
||||||
case 'string':
|
|
||||||
return ['=', 'contains', 'like']
|
|
||||||
.map((operatorType: String) => ({operatorType: operatorType, acceptsValues: 'any'}) as Operator);
|
|
||||||
case 'boolean':
|
|
||||||
return ['=', '!=']
|
|
||||||
.map((operatorType: String) => ({operatorType: operatorType, acceptsValues: 'boolean'}) as Operator);
|
|
||||||
case 'flag':
|
|
||||||
return ['=', '!=']
|
|
||||||
.map((operatorType: String) => ({operatorType: operatorType, acceptsValues: 'flag'}) as Operator);
|
|
||||||
case 'grade':
|
|
||||||
return ['=', '!=']
|
|
||||||
.map((operatorType: String) => ({operatorType: operatorType, acceptsValues: 'grade'}) as Operator);
|
|
||||||
case 'datetime':
|
|
||||||
return ['before', 'after']
|
|
||||||
.map((operatorType: String) => ({operatorType: operatorType, acceptsValues: 'datetime'}) as Operator);
|
|
||||||
case 'playtime':
|
|
||||||
return ['>', '<', '>=', '<=']
|
|
||||||
.map((operatorType: String) => ({operatorType: operatorType, acceptsValues: 'any'}) as Operator);
|
|
||||||
default:
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addPredicate(): void {
|
addPredicate(): void {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user