Big refactor of layout etc
This commit is contained in:
parent
30fb5a96ce
commit
b6a3c7b58b
@ -1,20 +1,12 @@
|
||||
<div class="btn-group w-100" ngbDropdown role="group" #dropdown="ngbDropdown">
|
||||
<div class="btn-group w-100" ngbDropdown role="group" #dropdown="ngbDropdown" (openChange)="onOpenChange($event)">
|
||||
<button class="btn btn-sm btn-outline-primary" id="dropdown_{{name}}" ngbDropdownToggle [disabled]="disabled">
|
||||
<i-bs name="{{icon}}"></i-bs>
|
||||
<div class="d-none d-sm-inline"> {{title}}</div>
|
||||
@if (selectionModel.queries.length > 0) {
|
||||
<pngx-clearable-badge [selected]="selectionModel.queries.length > 0" (cleared)="reset()"></pngx-clearable-badge>
|
||||
@if (isActive) {
|
||||
<pngx-clearable-badge [selected]="isActive" (cleared)="reset()"></pngx-clearable-badge>
|
||||
}
|
||||
</button>
|
||||
<div class="dropdown-menu px-3 shadow" ngbDropdownMenu attr.aria-labelledby="dropdown_{{name}}">
|
||||
<div class="btn-group my-2 w-100">
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" (click)="addAtom()" [disabled]="disabled">
|
||||
<i-bs name="node-plus"></i-bs> Add query
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" (click)="addExpression()" [disabled]="disabled">
|
||||
<i-bs name="braces"></i-bs> Add expression
|
||||
</button>
|
||||
</div>
|
||||
<div class="list-group list-group-flush">
|
||||
@for (query of selectionModel.queries; track query; let i = $index) {
|
||||
<div class="list-group-item px-0 d-flex flex-nowrap">
|
||||
@ -61,36 +53,46 @@
|
||||
<input class="w-25 form-control rounded-end" type="text" [(ngModel)]="query.value" [disabled]="disabled">
|
||||
}
|
||||
}
|
||||
<button class="btn btn-link btn-sm text-danger pe-0" type="button" (click)="removeComponent(query)" [disabled]="disabled">
|
||||
<button class="btn btn-link btn-sm text-danger pe-0" type="button" (click)="removeElement(query)" [disabled]="disabled">
|
||||
<i-bs name="x-circle"></i-bs>
|
||||
</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #queryExpression let-query="query">
|
||||
<div class="d-flex flex-column border border-primary rounded px-2 pt-2 w-100">
|
||||
<div class="btn-group btn-group-xs" role="group">
|
||||
<input [(ngModel)]="query.operator" type="radio" class="btn-check" id="logicalOperatorAnd_{{query.field}}" name="logicalOperatorAnd_{{query.field}}" value="AND">
|
||||
<label class="btn btn-outline-primary" for="logicalOperatorAnd_{{query.field}}" i18n>And</label>
|
||||
<input [(ngModel)]="query.operator" type="radio" class="btn-check" id="logicalOperatorOr_{{query.field}}" name="logicalOperatorOr_{{query.field}}" value="OR">
|
||||
<label class="btn btn-outline-primary" for="logicalOperatorOr_{{query.field}}" i18n>Or</label>
|
||||
<div class="d-flex w-100">
|
||||
<div class="d-flex flex-grow-1 flex-column">
|
||||
<div class="btn-group btn-group-xs" role="group">
|
||||
<input [(ngModel)]="query.operator" type="radio" class="btn-check" id="logicalOperatorOr_{{query.field}}" name="logicalOperatorOr_{{query.field}}" value="OR">
|
||||
<label class="btn btn-outline-primary" for="logicalOperatorOr_{{query.field}}" i18n>Any</label>
|
||||
<input [(ngModel)]="query.operator" type="radio" class="btn-check" id="logicalOperatorAnd_{{query.field}}" name="logicalOperatorAnd_{{query.field}}" value="AND">
|
||||
<label class="btn btn-outline-primary" for="logicalOperatorAnd_{{query.field}}" i18n>All</label>
|
||||
</div>
|
||||
<div class="list-group list-group-flush mb-n2">
|
||||
@for (subquery of query.value; track subquery; let i = $index) {
|
||||
<div class="list-group-item px-0 d-flex flex-nowrap">
|
||||
@switch (subquery.type) {
|
||||
@case (CustomFieldQueryComponentType.Atom) {
|
||||
<ng-container *ngTemplateOutlet="queryAtom; context: { query: subquery }"></ng-container>
|
||||
}
|
||||
@case (CustomFieldQueryComponentType.Expression) {
|
||||
<ng-container *ngTemplateOutlet="queryExpression; context: { query: subquery }"></ng-container>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-group list-group-flush">
|
||||
@for (subquery of query.value; track subquery; let i = $index) {
|
||||
<div class="list-group-item px-0 d-flex flex-nowrap">
|
||||
@switch (subquery.type) {
|
||||
@case (CustomFieldQueryComponentType.Atom) {
|
||||
<ng-container *ngTemplateOutlet="queryAtom; context: { query: subquery }"></ng-container>
|
||||
}
|
||||
@case (CustomFieldQueryComponentType.Expression) {
|
||||
<ng-container *ngTemplateOutlet="queryExpression; context: { query: subquery }"></ng-container>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<div class="btn-group-vertical ms-2 ps-2 border-start" role="group" aria-label="Vertical button group">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary text-primary" title="Add query" i18n-title (click)="addAtom(query)" [disabled]="disabled">
|
||||
<i-bs name="node-plus"></i-bs>
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary text-primary" title="Add expression" i18n-title (click)="addExpression(query)" [disabled]="disabled">
|
||||
<i-bs name="braces"></i-bs>
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary text-danger" (click)="removeElement(query)" [disabled]="disabled">
|
||||
<i-bs name="x-circle"></i-bs>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-link btn-sm text-danger pe-0" type="button" (click)="removeComponent(query)" [disabled]="disabled">
|
||||
<i-bs name="x-circle"></i-bs>
|
||||
</button>
|
||||
</ng-template>
|
||||
|
@ -10,11 +10,12 @@ import {
|
||||
CUSTOM_FIELD_QUERY_OPERATORS_BY_GROUP,
|
||||
CustomFieldQueryOperatorGroups,
|
||||
CUSTOM_FIELD_QUERY_OPERATOR_LABELS,
|
||||
CustomFieldQueryElement,
|
||||
} from 'src/app/data/custom-field-query'
|
||||
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
|
||||
|
||||
export class CustomFieldQueriesModel {
|
||||
public queries: Array<CustomFieldQueryAtom | CustomFieldQueryExpression> = []
|
||||
public queries: CustomFieldQueryElement[] = []
|
||||
|
||||
public readonly changed = new Subject<CustomFieldQueriesModel>()
|
||||
|
||||
@ -25,22 +26,11 @@ export class CustomFieldQueriesModel {
|
||||
}
|
||||
}
|
||||
|
||||
public addQuery(
|
||||
query: CustomFieldQueryAtom = new CustomFieldQueryAtom([
|
||||
null,
|
||||
CustomFieldQueryOperator.Exists,
|
||||
'true',
|
||||
])
|
||||
) {
|
||||
if (this.queries.length > 0) {
|
||||
if (this.queries[0].type === CustomFieldQueryElementType.Expression) {
|
||||
;(this.queries[0].value as Array<any>).push(query)
|
||||
} else {
|
||||
this.queries.push(query)
|
||||
}
|
||||
} else {
|
||||
this.queries.push(query)
|
||||
public addQuery(query: CustomFieldQueryAtom) {
|
||||
if (this.queries.length === 0) {
|
||||
this.addExpression()
|
||||
}
|
||||
;(this.queries[0].value as CustomFieldQueryElement[]).push(query)
|
||||
query.changed.subscribe(() => {
|
||||
if (query.field && query.operator && query.value) {
|
||||
this.changed.next(this)
|
||||
@ -52,15 +42,10 @@ export class CustomFieldQueriesModel {
|
||||
expression: CustomFieldQueryExpression = new CustomFieldQueryExpression()
|
||||
) {
|
||||
if (this.queries.length > 0) {
|
||||
if (this.queries[0].type === CustomFieldQueryElementType.Atom) {
|
||||
expression.value = this.queries as CustomFieldQueryAtom[]
|
||||
this.queries = []
|
||||
this.queries.push(expression)
|
||||
} else {
|
||||
;((this.queries[0] as CustomFieldQueryExpression).value as any[]).push(
|
||||
expression
|
||||
)
|
||||
}
|
||||
;(
|
||||
(this.queries[0] as CustomFieldQueryExpression)
|
||||
.value as CustomFieldQueryElement[]
|
||||
).push(expression)
|
||||
} else {
|
||||
this.queries.push(expression)
|
||||
}
|
||||
@ -69,20 +54,12 @@ export class CustomFieldQueriesModel {
|
||||
})
|
||||
}
|
||||
|
||||
private findComponent(
|
||||
queryComponent: CustomFieldQueryAtom | CustomFieldQueryExpression,
|
||||
components: any[]
|
||||
) {
|
||||
for (let i = 0; i < components.length; i++) {
|
||||
if (components[i] === queryComponent) {
|
||||
return components.splice(i, 1)[0]
|
||||
} else if (
|
||||
components[i].type === CustomFieldQueryElementType.Expression
|
||||
) {
|
||||
let found = this.findComponent(
|
||||
queryComponent,
|
||||
components[i].value as any[]
|
||||
)
|
||||
private findElement(queryElement: CustomFieldQueryElement, elements: any[]) {
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
if (elements[i] === queryElement) {
|
||||
return elements.splice(i, 1)[0]
|
||||
} else if (elements[i].type === CustomFieldQueryElementType.Expression) {
|
||||
let found = this.findElement(queryElement, elements[i].value as any[])
|
||||
if (found !== undefined) {
|
||||
return found
|
||||
}
|
||||
@ -91,17 +68,15 @@ export class CustomFieldQueriesModel {
|
||||
return undefined
|
||||
}
|
||||
|
||||
public removeComponent(
|
||||
queryComponent: CustomFieldQueryAtom | CustomFieldQueryExpression
|
||||
) {
|
||||
public removeElement(queryElement: CustomFieldQueryElement) {
|
||||
let foundComponent
|
||||
for (let i = 0; i < this.queries.length; i++) {
|
||||
let query = this.queries[i]
|
||||
if (query === queryComponent) {
|
||||
if (query === queryElement) {
|
||||
foundComponent = this.queries.splice(i, 1)[0]
|
||||
break
|
||||
} else if (query.type === CustomFieldQueryElementType.Expression) {
|
||||
let found = this.findComponent(queryComponent, query.value as any[])
|
||||
let found = this.findElement(queryElement, query.value as any[])
|
||||
if (found !== undefined) {
|
||||
foundComponent = found
|
||||
}
|
||||
@ -149,10 +124,14 @@ export class CustomFieldsQueryDropdownComponent {
|
||||
@Input()
|
||||
disabled: boolean = false
|
||||
|
||||
_selectionModel: CustomFieldQueriesModel = new CustomFieldQueriesModel()
|
||||
private _selectionModel: CustomFieldQueriesModel =
|
||||
new CustomFieldQueriesModel()
|
||||
|
||||
@Input()
|
||||
set selectionModel(model: CustomFieldQueriesModel) {
|
||||
if (this._selectionModel) {
|
||||
this._selectionModel.changed.complete()
|
||||
}
|
||||
model.changed.subscribe((updatedModel) => {
|
||||
this.selectionModelChange.next(updatedModel)
|
||||
})
|
||||
@ -172,6 +151,7 @@ export class CustomFieldsQueryDropdownComponent {
|
||||
|
||||
constructor(protected customFieldsService: CustomFieldsService) {
|
||||
this.getFields()
|
||||
this.reset()
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
@ -179,6 +159,19 @@ export class CustomFieldsQueryDropdownComponent {
|
||||
this.unsubscribeNotifier.complete()
|
||||
}
|
||||
|
||||
public onOpenChange(open: boolean) {
|
||||
if (open && this.selectionModel.queries.length === 0) {
|
||||
this.selectionModel.addExpression()
|
||||
}
|
||||
}
|
||||
|
||||
public get isActive(): boolean {
|
||||
return (
|
||||
(this.selectionModel.queries[0] as CustomFieldQueryExpression)?.value
|
||||
?.length > 0
|
||||
)
|
||||
}
|
||||
|
||||
private getFields() {
|
||||
this.customFieldsService
|
||||
.listAll()
|
||||
@ -188,22 +181,23 @@ export class CustomFieldsQueryDropdownComponent {
|
||||
})
|
||||
}
|
||||
|
||||
public addAtom() {
|
||||
this.selectionModel.addQuery()
|
||||
public addAtom(expression: CustomFieldQueryExpression) {
|
||||
expression.addAtom()
|
||||
}
|
||||
|
||||
public addExpression() {
|
||||
this.selectionModel.addExpression()
|
||||
public addExpression(expression: CustomFieldQueryExpression) {
|
||||
expression.addExpression()
|
||||
}
|
||||
|
||||
public removeComponent(
|
||||
component: CustomFieldQueryAtom | CustomFieldQueryExpression
|
||||
) {
|
||||
this.selectionModel.removeComponent(component)
|
||||
this.selectionModel.removeElement(component)
|
||||
}
|
||||
|
||||
public reset() {
|
||||
this.selectionModel.clear()
|
||||
this.selectionModel.clear(false)
|
||||
this.selectionModel.changed.next(this.selectionModel)
|
||||
}
|
||||
|
||||
getOperatorsForField(
|
||||
|
@ -44,6 +44,7 @@ export enum CustomFieldQueryOperatorGroups {
|
||||
String = 'string',
|
||||
Arithmetic = 'arithmetic',
|
||||
Containment = 'containment',
|
||||
Date = 'date',
|
||||
}
|
||||
|
||||
export const CUSTOM_FIELD_QUERY_OPERATORS_BY_GROUP = {
|
||||
@ -63,11 +64,15 @@ export const CUSTOM_FIELD_QUERY_OPERATORS_BY_GROUP = {
|
||||
CustomFieldQueryOperator.GreaterThanOrEqual,
|
||||
CustomFieldQueryOperator.LessThan,
|
||||
CustomFieldQueryOperator.LessThanOrEqual,
|
||||
CustomFieldQueryOperator.Range,
|
||||
// CustomFieldQueryOperator.Range,
|
||||
],
|
||||
[CustomFieldQueryOperatorGroups.Containment]: [
|
||||
CustomFieldQueryOperator.Contains,
|
||||
],
|
||||
[CustomFieldQueryOperatorGroups.Date]: [
|
||||
CustomFieldQueryOperator.GreaterThanOrEqual,
|
||||
CustomFieldQueryOperator.LessThanOrEqual,
|
||||
],
|
||||
}
|
||||
|
||||
// filters.py > SUPPORTED_EXPR_CATEGORIES
|
||||
@ -82,7 +87,7 @@ export const CUSTOM_FIELD_QUERY_OPERATOR_GROUPS_BY_TYPE = {
|
||||
],
|
||||
[CustomFieldDataType.Date]: [
|
||||
CustomFieldQueryOperatorGroups.Basic,
|
||||
CustomFieldQueryOperatorGroups.Arithmetic,
|
||||
CustomFieldQueryOperatorGroups.Date,
|
||||
],
|
||||
[CustomFieldDataType.Boolean]: [CustomFieldQueryOperatorGroups.Basic],
|
||||
[CustomFieldDataType.Integer]: [
|
||||
@ -232,13 +237,13 @@ export class CustomFieldQueryAtom extends CustomFieldQueryElement {
|
||||
export class CustomFieldQueryExpression extends CustomFieldQueryElement {
|
||||
constructor(
|
||||
expressionArray: [CustomFieldQueryLogicalOperator, any[]] = [
|
||||
CustomFieldQueryLogicalOperator.And,
|
||||
CustomFieldQueryLogicalOperator.Or,
|
||||
null,
|
||||
]
|
||||
) {
|
||||
super(CustomFieldQueryElementType.Expression)
|
||||
;[this._operator] = expressionArray
|
||||
let values = expressionArray[1]
|
||||
let values
|
||||
;[this._operator, values] = expressionArray
|
||||
if (!values) {
|
||||
this._value = []
|
||||
} else if (values?.length > 0 && values[0] instanceof Array) {
|
||||
@ -279,4 +284,26 @@ export class CustomFieldQueryExpression extends CustomFieldQueryElement {
|
||||
(this._value as any[]).every((v) => v.isValid)
|
||||
)
|
||||
}
|
||||
|
||||
public addAtom(
|
||||
atom: CustomFieldQueryAtom = new CustomFieldQueryAtom([
|
||||
null,
|
||||
CustomFieldQueryOperator.Exists,
|
||||
'true',
|
||||
])
|
||||
) {
|
||||
;(this._value as CustomFieldQueryElement[]).push(atom)
|
||||
atom.changed.subscribe(() => {
|
||||
this.changed.next(this)
|
||||
})
|
||||
}
|
||||
|
||||
public addExpression(
|
||||
expression: CustomFieldQueryExpression = new CustomFieldQueryExpression()
|
||||
) {
|
||||
;(this._value as CustomFieldQueryElement[]).push(expression)
|
||||
expression.changed.subscribe(() => {
|
||||
this.changed.next(this)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user