avoid the nasty regex

This commit is contained in:
shamoon committed 2026-10-25 10:00:54 -07:00
1 parent 646118cc98
commit f2b1b4c7e0
2 files changed
+47 -37

No files matched your search

@@ -39,10 +39,12 @@
</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}" />
@if (relativeDate.unit.includes('{n}')) {
<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 })"
@@ -49,15 +49,26 @@ 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 {
// The expression sent to the API, with {n} standing in for the amount
amount: number | null
unit: string
}
function parseRelativeDate(
value: string,
unit: string
): RelativeDate | undefined {
const [prefix, suffix] = unit.split('{n}')
if (suffix === undefined) {
return value === unit ? { amount: null, unit } : undefined
}
if (!value.startsWith(prefix) || !value.endsWith(suffix)) return undefined
const amount = value.slice(prefix.length, value.length - suffix.length)
if (!amount || !Number.isInteger(+amount) || +amount < 0) return undefined
return { amount: +amount, unit }
}
export class CustomFieldQueriesModel {
private readonly _queries = signal<CustomFieldQueryElement[]>([])
private rootSubscriptions: Subscription[] = []
@@ -315,30 +326,27 @@ export class CustomFieldsQueryDropdownComponent extends LoadingComponentWithPerm
public readonly today: string = new Date().toLocaleDateString('en-CA')
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` },
{ id: 'today', label: $localize`Today` },
{ id: '{n} days ago', label: $localize`days ago` },
{ id: '{n} weeks ago', label: $localize`weeks ago` },
{ id: '{n} months ago', label: $localize`months ago` },
{ id: '{n} years ago', label: $localize`years ago` },
{ id: 'in {n} days', label: $localize`days from now` },
{ id: 'in {n} weeks', label: $localize`weeks from now` },
{ id: 'in {n} months', label: $localize`months from now` },
{ id: 'in {n} years', label: $localize`years from now` },
]
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` }
)
if (!this.relativeDates.has(atom.id) && typeof atom.value === 'string') {
for (const { id } of this.relativeDateUnits) {
const relativeDate = parseRelativeDate(atom.value, id)
if (relativeDate) {
this.relativeDates.set(atom.id, relativeDate)
break
}
}
}
return this.relativeDates.get(atom.id)
@@ -349,7 +357,7 @@ export class CustomFieldsQueryDropdownComponent extends LoadingComponentWithPerm
this.relativeDates.delete(atom.id)
atom.value = null
} else {
this.relativeDates.set(atom.id, { amount: null, unit: 'days ago' })
this.relativeDates.set(atom.id, { amount: null, unit: '{n} days ago' })
this.updateRelativeDate(atom, {})
}
}
@@ -358,15 +366,15 @@ export class CustomFieldsQueryDropdownComponent extends LoadingComponentWithPerm
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}`
const { amount, unit } = Object.assign(
this.relativeDates.get(atom.id),
change
)
if (!unit.includes('{n}')) {
atom.value = unit
} else {
atom.value = amount === null ? null : unit.replace('{n}', `${amount}`)
}
}
public customFieldSearchFn = (term: string, field: CustomField): boolean =>