]> git.ipfire.org Git - thirdparty/paperless-ngx.git/blob
57aff1bd926854b77514830f46bb56f5912098e4
[thirdparty/paperless-ngx.git] /
1 <div class="btn-group w-100" role="group" ngbDropdown #dropdown="ngbDropdown" (openChange)="onOpenChange($event)" [popperOptions]="popperOptions">
2 <button class="btn btn-sm btn-outline-primary" id="dropdown_toggle" ngbDropdownToggle [disabled]="disabled">
3 <i-bs name="{{icon}}"></i-bs>
4 <div class="d-none d-sm-inline">&nbsp;{{title}}</div>
5 @if (isActive) {
6 <pngx-clearable-badge [selected]="isActive" (cleared)="reset()"></pngx-clearable-badge>
7 }
8 </button>
9 <div class="px-3 shadow" ngbDropdownMenu attr.aria-labelledby="dropdown_{{name}}">
10 <div class="list-group list-group-flush">
11 @for (element of selectionModel.queries; track element.id; let i = $index) {
12 <div class="list-group-item px-0 d-flex flex-nowrap">
13 @switch (element.type) {
14 @case (CustomFieldQueryComponentType.Atom) {
15 <ng-container *ngTemplateOutlet="queryAtom; context: { atom: element }"></ng-container>
16 }
17 @case (CustomFieldQueryComponentType.Expression) {
18 <ng-container *ngTemplateOutlet="queryExpression; context: { expression: element }"></ng-container>
19 }
20 }
21 </div>
22 }
23 </div>
24 </div>
25 </div>
26
27 <ng-template #comparisonValueTemplate let-atom="atom">
28 @if (getCustomFieldByID(atom.field)?.data_type === CustomFieldDataType.Date) {
29 <input class="form-control" placeholder="yyyy-mm-dd"
30 [(ngModel)]="atom.value"
31 ngbDatepicker
32 #d="ngbDatepicker"
33 [footerTemplate]="datePickerFooterTemplate" />
34 <button class="btn btn-sm btn-outline-secondary rounded-end" (click)="d.toggle()" type="button">
35 <i-bs name="calendar-event"></i-bs>
36 </button>
37 <ng-template #datePickerFooterTemplate>
38 <div class="btn-group-xs border-top p-2 d-flex">
39 <button type="button" class="btn btn-primary" (click)="atom.value = today; d.close()" i18n>Today</button>
40 <button type="button" class="btn btn-secondary ms-auto" (click)="d.close()" i18n>Close</button>
41 </div>
42 </ng-template>
43 } @else if (getCustomFieldByID(atom.field)?.data_type === CustomFieldDataType.Float || getCustomFieldByID(atom.field)?.data_type === CustomFieldDataType.Integer) {
44 <input class="w-25 form-control rounded-end" type="number" [(ngModel)]="atom.value" [disabled]="disabled">
45 } @else if (getCustomFieldByID(atom.field)?.data_type === CustomFieldDataType.Boolean) {
46 <select class="w-25 form-select rounded-end" [(ngModel)]="atom.value" [disabled]="disabled">
47 <option value="true" i18n>True</option>
48 <option value="false" i18n>False</option>
49 </select>
50 } @else if (getCustomFieldByID(atom.field)?.data_type === CustomFieldDataType.Select) {
51 <ng-select #fieldSelects
52 class="paperless-input-select rounded-end"
53 [items]="getSelectOptionsForField(atom.field)"
54 bindLabel="label"
55 bindValue="id"
56 [(ngModel)]="atom.value"
57 [disabled]="disabled"
58 (mousedown)="$event.stopImmediatePropagation()"
59 ></ng-select>
60 } @else if (getCustomFieldByID(atom.field)?.data_type === CustomFieldDataType.DocumentLink) {
61 <pngx-input-document-link [(ngModel)]="atom.value" class="w-25 form-select doc-link-select p-0" placeholder="Search docs..." i18n-placeholder [minimal]="true"></pngx-input-document-link>
62 } @else {
63 <input class="w-25 form-control rounded-end" type="text" [(ngModel)]="atom.value" [disabled]="disabled">
64 }
65 </ng-template>
66
67 <ng-template #queryAtom let-atom="atom">
68 <div class="input-group input-group-sm">
69 <ng-select
70 class="paperless-input-select"
71 [items]="customFields"
72 [(ngModel)]="atom.field"
73 [disabled]="disabled"
74 bindLabel="name"
75 bindValue="id"
76 (mousedown)="$event.stopImmediatePropagation()"
77 ></ng-select>
78 <select class="w-25 form-select" [(ngModel)]="atom.operator" [disabled]="disabled">
79 @for (operator of getOperatorsForField(atom.field); track operator.label) {
80 <option [ngValue]="operator.value">{{operator.label}}</option>
81 }
82 </select>
83 @switch (atom.operator) {
84 @case (CustomFieldQueryOperator.Exists) {
85 <select class="w-25 form-select rounded-end" [(ngModel)]="atom.value" [disabled]="disabled">
86 <option value="true" i18n>True</option>
87 <option value="false" i18n>False</option>
88 </select>
89 }
90 @case (CustomFieldQueryOperator.IsNull) {
91 <select class="w-25 form-select rounded-end" [(ngModel)]="atom.value" [disabled]="disabled">
92 <option value="true" i18n>True</option>
93 <option value="false" i18n>False</option>
94 </select>
95 }
96 @case (CustomFieldQueryOperator.GreaterThanOrEqual) {
97 <ng-container *ngTemplateOutlet="comparisonValueTemplate; context: { atom: atom }"></ng-container>
98 }
99 @case (CustomFieldQueryOperator.LessThanOrEqual) {
100 <ng-container *ngTemplateOutlet="comparisonValueTemplate; context: { atom: atom }"></ng-container>
101 }
102 @case (CustomFieldQueryOperator.GreaterThan) {
103 <ng-container *ngTemplateOutlet="comparisonValueTemplate; context: { atom: atom }"></ng-container>
104 }
105 @case (CustomFieldQueryOperator.LessThan) {
106 <ng-container *ngTemplateOutlet="comparisonValueTemplate; context: { atom: atom }"></ng-container>
107 }
108 @case (CustomFieldQueryOperator.Contains) {
109 <pngx-input-document-link [(ngModel)]="atom.value" class="w-25 form-select doc-link-select p-0" placeholder="Search docs..." i18n-placeholder [minimal]="true"></pngx-input-document-link>
110 }
111 @case (CustomFieldQueryOperator.In) {
112 <ng-select
113 class="paperless-input-select rounded-end"
114 [items]="getSelectOptionsForField(atom.field)"
115 bindLabel="label"
116 bindValue="id"
117 [(ngModel)]="atom.value"
118 [disabled]="disabled"
119 [multiple]="true"
120 (mousedown)="$event.stopImmediatePropagation()"
121 ></ng-select>
122 }
123 @case (CustomFieldQueryOperator.Exact) {
124 <ng-container *ngTemplateOutlet="comparisonValueTemplate; context: { atom: atom }"></ng-container>
125 }
126 @default {
127 <input class="w-25 form-control rounded-end" type="text" [(ngModel)]="atom.value" [disabled]="disabled">
128 }
129 }
130 <button class="btn btn-link btn-sm text-danger pe-0" type="button" (click)="removeElement(atom)" [disabled]="disabled">
131 <i-bs name="x-circle"></i-bs>
132 </button>
133 </div>
134 </ng-template>
135
136 <ng-template #queryExpression let-expression="expression">
137 <div class="d-flex w-100">
138 <div class="d-flex flex-grow-1 flex-column">
139 <div class="btn-group btn-group-xs" role="group">
140 <input [(ngModel)]="expression.operator" type="radio" class="btn-check" id="logicalOperatorOr_{{expression.id}}" name="logicalOperatorOr_{{expression.id}}" value="OR" [disabled]="expression.depth > 0 && expression.value.length < 2">
141 <label class="btn btn-outline-primary" for="logicalOperatorOr_{{expression.id}}" i18n>Any</label>
142 <input [(ngModel)]="expression.operator" type="radio" class="btn-check" id="logicalOperatorAnd_{{expression.id}}" name="logicalOperatorAnd_{{expression.id}}" value="AND" [disabled]="expression.depth > 0 && expression.value.length < 2">
143 <label class="btn btn-outline-primary" for="logicalOperatorAnd_{{expression.id}}" i18n>All</label>
144 @if (expression.negatable) {
145 <input [(ngModel)]="expression.operator" type="radio" class="btn-check" id="logicalOperatorNot_{{expression.id}}" name="logicalOperatorNot_{{expression.id}}" value="NOT">
146 <label class="btn btn-outline-secondary" for="logicalOperatorNot_{{expression.id}}" i18n>Not</label>
147 }
148 </div>
149 <div class="list-group list-group-flush mb-n2">
150 @for (element of expression.value; track element.id; let i = $index) {
151 <div class="list-group-item px-0 d-flex flex-nowrap">
152 @switch (element.type) {
153 @case (CustomFieldQueryComponentType.Atom) {
154 <ng-container *ngTemplateOutlet="queryAtom; context: { atom: element }"></ng-container>
155 }
156 @case (CustomFieldQueryComponentType.Expression) {
157 <ng-container *ngTemplateOutlet="queryExpression; context: { expression: element }"></ng-container>
158 }
159 }
160 </div>
161 }
162 </div>
163 </div>
164 <div class="btn-group-vertical ms-2 ps-2 border-start" role="group" aria-label="Vertical button group">
165 <button type="button" class="btn btn-sm btn-outline-secondary text-primary" title="Add query" i18n-title (click)="addAtom(expression)" [disabled]="disabled || expression.value.length === CUSTOM_FIELD_QUERY_MAX_ATOMS">
166 <i-bs name="node-plus"></i-bs>
167 </button>
168 <button type="button" class="btn btn-sm btn-outline-secondary text-primary" title="Add expression" i18n-title (click)="addExpression(expression)" [disabled]="disabled || expression.depth === CUSTOM_FIELD_QUERY_MAX_DEPTH">
169 <i-bs name="braces"></i-bs>
170 </button>
171 @if (expression.depth > 0) {
172 <button type="button" class="btn btn-sm btn-outline-secondary text-danger" (click)="removeElement(expression)" [disabled]="disabled">
173 <i-bs name="x-circle"></i-bs>
174 </button>
175 }
176 </div>
177 </div>
178 </ng-template>