1
0

fix(filters): lint

This commit is contained in:
kolaente
2024-03-09 19:24:17 +01:00
parent 6fea5640e8
commit 4e6e0608c7
13 changed files with 149 additions and 443 deletions

View File

@ -15,7 +15,7 @@ function initState(value: string) {
:init-state="initState('dueDate < now && done = false && dueDate > now/w+1w')"
>
<template #default="{state}">
<FilterInput v-model="state.value"/>
<FilterInput v-model="state.value" />
</template>
</Variant>
</Story>

View File

@ -3,8 +3,6 @@ import {computed, nextTick, ref, watch} from 'vue'
import {useAutoHeightTextarea} from '@/composables/useAutoHeightTextarea'
import DatepickerWithValues from '@/components/date/datepickerWithValues.vue'
import UserService from '@/services/user'
import {getAvatarUrl, getDisplayName} from '@/models/user'
import {createRandomID} from '@/helpers/randomId'
import AutocompleteDropdown from '@/components/input/AutocompleteDropdown.vue'
import {useLabelStore} from '@/stores/labels'
import XLabel from '@/components/tasks/partials/label.vue'
@ -169,19 +167,21 @@ function updateDateInQuery(newDate: string) {
const autocompleteMatchPosition = ref(0)
const autocompleteMatchText = ref('')
const autocompleteResultType = ref<'labels' | 'assignees' | 'projects' | null>(null)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const autocompleteResults = ref<any[]>([])
const labelStore = useLabelStore()
const projectStore = useProjectStore()
function handleFieldInput(e, autocompleteOnInput) {
function handleFieldInput() {
const cursorPosition = filterInput.value.selectionStart
const textUpToCursor = filterQuery.value.substring(0, cursorPosition)
AUTOCOMPLETE_FIELDS.forEach(field => {
const pattern = new RegExp('(' + field + '\\s*' + FILTER_OPERATORS_REGEX + '\\s*)([\'"]?)([^\'"&\|\(\)]+\\1?)?$', 'ig')
const pattern = new RegExp('(' + field + '\\s*' + FILTER_OPERATORS_REGEX + '\\s*)([\'"]?)([^\'"&|()]+\\1?)?$', 'ig')
const match = pattern.exec(textUpToCursor)
if (match !== null) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [matched, prefix, operator, space, keyword] = match
if (keyword) {
if (matched.startsWith('label')) {
@ -229,40 +229,40 @@ function autocompleteSelect(value) {
@update:modelValue="autocompleteSelect"
>
<template
v-slot:input="{ onKeydown, onFocusField, onUpdateField }"
#input="{ onKeydown, onFocusField }"
>
<div class="control filter-input">
<textarea
@input="e => handleFieldInput(e, onUpdateField)"
@focus="onFocusField"
@keydown="onKeydown"
ref="filterInput"
v-model="filterQuery"
autocomplete="off"
autocorrect="off"
autocapitalize="off"
spellcheck="false"
v-model="filterQuery"
class="input"
:class="{'has-autocomplete-results': autocompleteResults.length > 0}"
ref="filterInput"
:placeholder="$t('filters.query.placeholder')"
@input="handleFieldInput"
@focus="onFocusField"
@keydown="onKeydown"
@blur="e => emit('blur', e)"
></textarea>
/>
<div
class="filter-input-highlight"
:style="{'height': height}"
v-html="highlightedFilterQuery"
></div>
/>
<DatepickerWithValues
v-model="currentDatepickerValue"
:open="datePickerPopupOpen"
@close="() => datePickerPopupOpen = false"
@update:model-value="updateDateInQuery"
@update:modelValue="updateDateInQuery"
/>
</div>
</template>
<template
v-slot:result="{ item }"
#result="{ item }"
>
<XLabel
v-if="autocompleteResultType === 'labels'"
@ -273,7 +273,9 @@ function autocompleteSelect(value) {
:user="item"
:avatar-size="25"
/>
<template v-else> {{ item.title }}</template>
<template v-else>
{{ item.title }}
</template>
</template>
</AutocompleteDropdown>
</div>

View File

@ -6,12 +6,18 @@ const showDocs = ref(false)
</script>
<template>
<BaseButton @click="showDocs = !showDocs" class="has-text-primary">
<BaseButton
class="has-text-primary"
@click="showDocs = !showDocs"
>
{{ $t('filters.query.help.link') }}
</BaseButton>
<Transition>
<div v-if="showDocs" class="content">
<div
v-if="showDocs"
class="content"
>
<p>{{ $t('filters.query.help.intro') }}</p>
<ul>
<li><code>done</code>: {{ $t('filters.query.help.fields.done') }}</li>
@ -47,11 +53,13 @@ const showDocs = ref(false)
<ul>
<li><code>priority = 4</code>: {{ $t('filters.query.help.examples.priorityEqual') }}</li>
<li><code>dueDate &lt; now</code>: {{ $t('filters.query.help.examples.dueDatePast') }}</li>
<li><code>done = false &amp;&amp; priority &gt;= 3</code>:
<li>
<code>done = false &amp;&amp; priority &gt;= 3</code>:
{{ $t('filters.query.help.examples.undoneHighPriority') }}
</li>
<li><code>assignees in [user1, user2]</code>: {{ $t('filters.query.help.examples.assigneesIn') }}</li>
<li><code>(priority = 1 || priority = 2) &amp;&amp; dueDate &lt;= now</code>:
<li>
<code>(priority = 1 || priority = 2) &amp;&amp; dueDate &lt;= now</code>:
{{ $t('filters.query.help.examples.priorityOneOrTwoPastDue') }}
</li>
</ul>

View File

@ -25,6 +25,7 @@
v-model="value"
:has-title="true"
class="filter-popup"
@update:modelValue="emitChanges"
/>
</modal>
</template>
@ -36,28 +37,27 @@ import Filters from '@/components/project/partials/filters.vue'
import {getDefaultTaskFilterParams, type TaskFilterParams} from '@/services/taskCollection'
const modelValue = defineModel<TaskFilterParams>()
const modelValue = defineModel<TaskFilterParams>({})
const value = computed<TaskFilterParams>({
get() {
return modelValue.value
},
set(value) {
if(modelValue === value) {
return
}
modelValue.value = value
},
})
const value = ref<TaskFilterParams>({})
watch(
() => modelValue,
(modelValue) => {
() => modelValue.value,
(modelValue: TaskFilterParams) => {
value.value = modelValue
},
{immediate: true},
)
function emitChanges(newValue: TaskFilterParams) {
if (modelValue.value?.filter === newValue.filter && modelValue.value?.s === newValue.s) {
return
}
modelValue.value.filter = newValue.filter
modelValue.value.s = newValue.s
}
const hasFilters = computed(() => {
// this.value also contains the page parameter which we don't want to include in filters
// eslint-disable-next-line no-unused-vars

View File

@ -4,12 +4,12 @@
:title="hasTitle ? $t('filters.title') : ''"
role="search"
>
<FilterInput
<FilterInput
v-model="params.filter"
:project-id="projectId"
@blur="change()"
/>
<div class="field is-flex is-flex-direction-column">
<Fancycheckbox
v-model="params.filter_include_nulls"
@ -18,10 +18,13 @@
{{ $t('filters.attributes.includeNulls') }}
</Fancycheckbox>
</div>
<FilterInputDocs/>
<template v-if="hasFooter" #footer>
<FilterInputDocs />
<template
v-if="hasFooter"
#footer
>
<x-button
variant="primary"
@click.prevent.stop="change()"
@ -48,25 +51,24 @@ import {useProjectStore} from '@/stores/projects'
import {FILTER_OPERATORS, transformFilterStringForApi, transformFilterStringFromApi} from '@/helpers/filters'
import FilterInputDocs from '@/components/project/partials/FilterInputDocs.vue'
const props = defineProps({
hasTitle: {
type: Boolean,
default: false,
},
hasFooter: {
type: Boolean,
default: true,
},
})
const {
hasTitle= false,
hasFooter = true,
modelValue,
} = defineProps<{
hasTitle?: boolean,
hasFooter?: boolean,
modelValue: TaskFilterParams,
}>()
const modelValue = defineModel<TaskFilterParams>()
const emit = defineEmits(['update:modelValue'])
const route = useRoute()
const projectId = computed(() => {
if (route.name?.startsWith('project.')) {
return Number(route.params.projectId)
}
return undefined
})
@ -80,7 +82,7 @@ const params = ref<TaskFilterParams>({
// Using watchDebounced to prevent the filter re-triggering itself.
watchDebounced(
() => modelValue.value,
() => modelValue,
(value: TaskFilterParams) => {
const val = {...value}
val.filter = transformFilterStringFromApi(
@ -102,22 +104,25 @@ function change() {
labelTitle => labelStore.filterLabelsByQuery([], labelTitle)[0]?.id || null,
projectTitle => projectStore.searchProject(projectTitle)[0]?.id || null,
)
let s = ''
// When the filter does not contain any filter tokens, assume a simple search and redirect the input
const hasFilterQueries = FILTER_OPERATORS.find(o => filter.includes(o)) || false
if (!hasFilterQueries) {
s = filter
}
modelValue.value = {
const newParams = {
...params.value,
filter: s === '' ? filter : '',
s,
}
if (JSON.stringify(modelValue) === JSON.stringify(newParams)) {
return
}
emit('update:modelValue', newParams)
}
</script>
<style lang="scss" scoped>
</style>