Ok do it on two lines, separate fields

This commit is contained in:
shamoon committed 2026-10-22 00:58:47 -07:00
1 parent 021c6421c7
commit 997ef6f7d6
2 files changed
+95 -41

No files matched your search

@@ -33,36 +33,42 @@
<ng-template #comparisonValueTemplate let-atom="atom">
@if (getCustomFieldByID(atom.field)?.data_type === CustomFieldDataType.Date) {
<div class="input-group input-group-sm w-100 order-1 mt-1">
<select class="form-select w-auto flex-grow-0" aria-label="Date value type" i18n-aria-label
[ngModel]="isRelativeDate(atom)" (ngModelChange)="setDateQueryMode(atom, $event)"
[ngModelOptions]="{standalone: true}" [disabled]="disabled">
<option [ngValue]="false" i18n>Fixed</option>
<option [ngValue]="true" i18n>Relative</option>
</select>
@if (isRelativeDate(atom)) {
<input class="form-control rounded-end" type="text" aria-label="Relative date" i18n-aria-label
placeholder="e.g. in 30 days" i18n-placeholder
[(ngModel)]="atom.value" [ngModelOptions]="{standalone: true}" [disabled]="disabled">
} @else {
<input class="form-control" placeholder="yyyy-mm-dd"
[(ngModel)]="atom.value"
[ngModelOptions]="{standalone: true}"
[disabled]="disabled"
ngbDatepicker
#d="ngbDatepicker"
[footerTemplate]="datePickerFooterTemplate" />
<button class="btn btn-sm btn-outline-secondary rounded-end" (click)="d.toggle()" type="button" [disabled]="disabled" aria-label="Open date picker" i18n-aria-label>
<i-bs name="calendar-event"></i-bs>
</button>
<ng-template #datePickerFooterTemplate>
<div class="btn-group-xs border-top p-2 d-flex">
<button type="button" class="btn btn-primary" (click)="atom.value = today; d.close()" i18n>Today</button>
<button type="button" class="btn btn-secondary ms-auto" (click)="d.close()" i18n>Close</button>
</div>
</ng-template>
}
</div>
@let relativeDate = getRelativeDate(atom);
<button class="btn btn-sm btn-outline-secondary" [class.active]="relativeDate" [class.rounded-end]="relativeDate" [attr.aria-pressed]="!!relativeDate" (click)="toggleRelativeDate(atom)" type="button" title="Relative date" i18n-title>
<i-bs name="clock-history"></i-bs>
</button>
@if (relativeDate) {
<div class="input-group input-group-sm w-100 order-1 mt-1">
<input class="form-control w-25 flex-grow-0" type="number" min="0" aria-label="Amount" i18n-aria-label
[ngModel]="relativeDate.amount"
(ngModelChange)="updateRelativeDate(atom, { amount: $event })"
[ngModelOptions]="{standalone: true}" />
<select class="form-select" aria-label="Unit" i18n-aria-label
[ngModel]="relativeDate.unit"
(ngModelChange)="updateRelativeDate(atom, { unit: $event })"
[ngModelOptions]="{standalone: true}">
@for (unit of relativeDateUnits; track unit.id) {
<option [ngValue]="unit.id">{{unit.label}}</option>
}
</select>
</div>
} @else {
<input class="form-control" placeholder="yyyy-mm-dd"
[(ngModel)]="atom.value"
[ngModelOptions]="{standalone: true}"
ngbDatepicker
#d="ngbDatepicker"
[footerTemplate]="datePickerFooterTemplate" />
<button class="btn btn-sm btn-outline-secondary rounded-end" (click)="d.toggle()" type="button" aria-label="Open date picker" i18n-aria-label>
<i-bs name="calendar-event"></i-bs>
</button>
<ng-template #datePickerFooterTemplate>
<div class="btn-group-xs border-top p-2 d-flex">
<button type="button" class="btn btn-primary" (click)="atom.value = today; d.close()" i18n>Today</button>
<button type="button" class="btn btn-secondary ms-auto" (click)="d.close()" i18n>Close</button>
</div>
</ng-template>
}
} @else if (getCustomFieldByID(atom.field)?.data_type === CustomFieldDataType.Float || getCustomFieldByID(atom.field)?.data_type === CustomFieldDataType.Integer) {
<input class="w-25 form-control rounded-end" type="number" [(ngModel)]="atom.value" [ngModelOptions]="{standalone: true}" [disabled]="disabled">
} @else if (getCustomFieldByID(atom.field)?.data_type === CustomFieldDataType.Boolean) {
@@ -49,6 +49,15 @@ import { LoadingComponentWithPermissions } from '../../loading-component/loading
import { ClearableBadgeComponent } from '../clearable-badge/clearable-badge.component'
import { DocumentLinkComponent } from '../input/document-link/document-link.component'
// Matches the English expressions built by updateRelativeDate, e.g. "30 days ago" / "in 2 weeks"
const RELATIVE_DATE_PATTERN =
/^(?:(\d+) (days|weeks|months|years) ago|in (\d+) (days|weeks|months|years))$/
interface RelativeDate {
amount: number | null
unit: string
}
export class CustomFieldQueriesModel {
private readonly _queries = signal<CustomFieldQueryElement[]>([])
private rootSubscriptions: Subscription[] = []
@@ -305,20 +314,59 @@ export class CustomFieldsQueryDropdownComponent extends LoadingComponentWithPerm
public readonly today: string = new Date().toLocaleDateString('en-CA')
private readonly relativeDateModes = new Map<string, boolean>()
public readonly relativeDateUnits = [
{ id: 'days ago', label: $localize`days ago` },
{ id: 'weeks ago', label: $localize`weeks ago` },
{ id: 'months ago', label: $localize`months ago` },
{ id: 'years ago', label: $localize`years ago` },
{ id: 'days from now', label: $localize`days from now` },
{ id: 'weeks from now', label: $localize`weeks from now` },
{ id: 'months from now', label: $localize`months from now` },
{ id: 'years from now', label: $localize`years from now` },
]
isRelativeDate(atom: CustomFieldQueryAtom): boolean {
return (
this.relativeDateModes.get(atom.id) ??
(typeof atom.value === 'string' &&
!!atom.value &&
!/^\d{4}-\d{2}-\d{2}$/.test(atom.value))
)
private readonly relativeDates = new Map<string, RelativeDate>()
getRelativeDate(atom: CustomFieldQueryAtom): RelativeDate | undefined {
if (!this.relativeDates.has(atom.id)) {
const match =
typeof atom.value === 'string' &&
atom.value.match(RELATIVE_DATE_PATTERN)
if (match) {
this.relativeDates.set(
atom.id,
match[1]
? { amount: +match[1], unit: `${match[2]} ago` }
: { amount: +match[3], unit: `${match[4]} from now` }
)
}
}
return this.relativeDates.get(atom.id)
}
setDateQueryMode(atom: CustomFieldQueryAtom, relative: boolean) {
this.relativeDateModes.set(atom.id, relative)
atom.value = null
toggleRelativeDate(atom: CustomFieldQueryAtom) {
if (this.getRelativeDate(atom)) {
this.relativeDates.delete(atom.id)
atom.value = null
} else {
this.relativeDates.set(atom.id, { amount: null, unit: 'days ago' })
this.updateRelativeDate(atom, {})
}
}
updateRelativeDate(
atom: CustomFieldQueryAtom,
change: Partial<RelativeDate>
) {
const relativeDate = Object.assign(this.relativeDates.get(atom.id), change)
const { amount, unit } = relativeDate
const [period, direction] = unit.split(' ', 2)
atom.value =
amount === null
? null
: direction === 'ago'
? `${amount} ${unit}`
: `in ${amount} ${period}`
}
public customFieldSearchFn = (term: string, field: CustomField): boolean =>