fix(filters): label highlighting and autocomplete fields now work with in operator
Previously, when creating a filter query with the 'in' operator and multiple values, autocompletion and highlighting was not available. This change now implements a split for each value, seperated by a comma.
This commit is contained in:
parent
0529f30e77
commit
dbfe162cd2
@ -46,7 +46,7 @@ The available operators for filtering include:
|
||||
* `<`: Less than
|
||||
* `<=`: Less than or equal to
|
||||
* `like`: Matches a pattern (using wildcard `%`)
|
||||
* `in`: Matches any value in a list
|
||||
* `in`: Matches any value in a comma-seperated list of values
|
||||
|
||||
To combine multiple conditions, you can use the following logical operators:
|
||||
|
||||
|
@ -16,7 +16,7 @@ import {
|
||||
AVAILABLE_FILTER_FIELDS,
|
||||
FILTER_JOIN_OPERATOR,
|
||||
FILTER_OPERATORS,
|
||||
FILTER_OPERATORS_REGEX, LABEL_FIELDS,
|
||||
FILTER_OPERATORS_REGEX, LABEL_FIELDS, getFilterFieldRegexPattern,
|
||||
} from '@/helpers/filters'
|
||||
|
||||
const {
|
||||
@ -104,15 +104,25 @@ const highlightedFilterQuery = computed(() => {
|
||||
})
|
||||
LABEL_FIELDS
|
||||
.forEach(f => {
|
||||
const pattern = new RegExp(f + '\\s*' + FILTER_OPERATORS_REGEX + '\\s*([\'"]?)([^\'"\\s]+\\1?)?', 'ig')
|
||||
highlighted = highlighted.replaceAll(pattern, (match, token, start, value) => {
|
||||
const pattern = getFilterFieldRegexPattern(f)
|
||||
highlighted = highlighted.replaceAll(pattern, (match, prefix, operator, space, value) => {
|
||||
|
||||
if (typeof value === 'undefined') {
|
||||
value = ''
|
||||
}
|
||||
|
||||
let labelTitles = [value]
|
||||
if(operator === 'in' || operator === '?=') {
|
||||
labelTitles = value.split(',').map(v => v.trim())
|
||||
}
|
||||
|
||||
const label = labelStore.getLabelsByExactTitles([value])[0] || undefined
|
||||
const labelsHtml: string[] = []
|
||||
labelTitles.forEach(t => {
|
||||
const label = labelStore.getLabelByExactTitle(t) || undefined
|
||||
labelsHtml.push(`<span class="filter-query__label_value" style="background-color: ${label?.hexColor}; color: ${label?.textColor}">${label?.title ?? t}</span>`)
|
||||
})
|
||||
|
||||
return `${f} ${token} <span class="filter-query__label_value" style="background-color: ${label?.hexColor}; color: ${label?.textColor}">${label?.title ?? value}<span>`
|
||||
return `${f} ${operator} ${labelsHtml.join(', ')}`
|
||||
})
|
||||
})
|
||||
FILTER_OPERATORS
|
||||
@ -184,26 +194,31 @@ function handleFieldInput() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [matched, prefix, operator, space, keyword] = match
|
||||
if (keyword) {
|
||||
let search = keyword
|
||||
if(operator === 'in' || operator === '?=') {
|
||||
const keywords = keyword.split(',')
|
||||
search = keywords[keywords.length - 1].trim()
|
||||
}
|
||||
if (matched.startsWith('label')) {
|
||||
autocompleteResultType.value = 'labels'
|
||||
autocompleteResults.value = labelStore.filterLabelsByQuery([], keyword)
|
||||
autocompleteResults.value = labelStore.filterLabelsByQuery([], search)
|
||||
}
|
||||
if (matched.startsWith('assignee')) {
|
||||
autocompleteResultType.value = 'assignees'
|
||||
if (projectId) {
|
||||
projectUserService.getAll({projectId}, {s: keyword})
|
||||
projectUserService.getAll({projectId}, {s: search})
|
||||
.then(users => autocompleteResults.value = users.length > 1 ? users : [])
|
||||
} else {
|
||||
userService.getAll({}, {s: keyword})
|
||||
userService.getAll({}, {s: search})
|
||||
.then(users => autocompleteResults.value = users.length > 1 ? users : [])
|
||||
}
|
||||
}
|
||||
if (!projectId && matched.startsWith('project')) {
|
||||
autocompleteResultType.value = 'projects'
|
||||
autocompleteResults.value = projectStore.searchProject(keyword)
|
||||
autocompleteResults.value = projectStore.searchProject(search)
|
||||
}
|
||||
autocompleteMatchText.value = keyword
|
||||
autocompleteMatchPosition.value = prefix.length - 1
|
||||
autocompleteMatchPosition.value = prefix.length - 1 + keyword.replace(search, '').length
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -57,7 +57,7 @@ export const FILTER_JOIN_OPERATOR = [
|
||||
|
||||
export const FILTER_OPERATORS_REGEX = '(<|>|<=|>=|=|!=|in)'
|
||||
|
||||
function getFieldPattern(field: string): RegExp {
|
||||
export function getFilterFieldRegexPattern(field: string): RegExp {
|
||||
return new RegExp('(' + field + '\\s*' + FILTER_OPERATORS_REGEX + '\\s*)([\'"]?)([^\'"&|()]+\\1?)?', 'ig')
|
||||
}
|
||||
|
||||
@ -73,7 +73,7 @@ export function transformFilterStringForApi(
|
||||
|
||||
// Transform labels to ids
|
||||
LABEL_FIELDS.forEach(field => {
|
||||
const pattern = getFieldPattern(field)
|
||||
const pattern = getFilterFieldRegexPattern(field)
|
||||
|
||||
let match: RegExpExecArray | null
|
||||
while ((match = pattern.exec(filter)) !== null) {
|
||||
@ -96,7 +96,7 @@ export function transformFilterStringForApi(
|
||||
})
|
||||
// Transform projects to ids
|
||||
PROJECT_FIELDS.forEach(field => {
|
||||
const pattern = getFieldPattern(field)
|
||||
const pattern = getFilterFieldRegexPattern(field)
|
||||
|
||||
let match: RegExpExecArray | null
|
||||
while ((match = pattern.exec(filter)) !== null) {
|
||||
@ -143,7 +143,7 @@ export function transformFilterStringFromApi(
|
||||
|
||||
// Transform labels to their titles
|
||||
LABEL_FIELDS.forEach(field => {
|
||||
const pattern = getFieldPattern(field)
|
||||
const pattern = getFilterFieldRegexPattern(field)
|
||||
|
||||
let match: RegExpExecArray | null
|
||||
while ((match = pattern.exec(filter)) !== null) {
|
||||
@ -167,7 +167,7 @@ export function transformFilterStringFromApi(
|
||||
|
||||
// Transform projects to ids
|
||||
PROJECT_FIELDS.forEach(field => {
|
||||
const pattern = getFieldPattern(field)
|
||||
const pattern = getFilterFieldRegexPattern(field)
|
||||
|
||||
let match: RegExpExecArray | null
|
||||
while ((match = pattern.exec(filter)) !== null) {
|
||||
|
@ -446,7 +446,7 @@
|
||||
"lessThan": "Less than",
|
||||
"lessThanOrEqual": "Less than or equal to",
|
||||
"like": "Matches a pattern (using wildcard %)",
|
||||
"in": "Matches any value in a list"
|
||||
"in": "Matches any value in a comma-seperated list of values"
|
||||
},
|
||||
"logicalOperators": {
|
||||
"intro": "To combine multiple conditions, you can use the following logical operators:",
|
||||
|
@ -57,6 +57,12 @@ export const useLabelStore = defineStore('label', () => {
|
||||
.values(labels.value)
|
||||
.filter(({title}) => labelTitles.some(l => l.toLowerCase() === title.toLowerCase()))
|
||||
})
|
||||
|
||||
const getLabelByExactTitle = computed(() => {
|
||||
return (labelTitle: string) => Object
|
||||
.values(labels.value)
|
||||
.find(l => l.title.toLowerCase() === labelTitle.toLowerCase())
|
||||
})
|
||||
|
||||
|
||||
function setIsLoading(newIsLoading: boolean) {
|
||||
@ -145,6 +151,7 @@ export const useLabelStore = defineStore('label', () => {
|
||||
getLabelById,
|
||||
filterLabelsByQuery,
|
||||
getLabelsByExactTitles,
|
||||
getLabelByExactTitle,
|
||||
|
||||
setLabels,
|
||||
setLabel,
|
||||
|
Loading…
x
Reference in New Issue
Block a user