Files
DiceCloud/app/imports/client/ui/components/tree/TreeSearchInput.vue
2023-06-26 14:45:19 +02:00

187 lines
4.5 KiB
Vue

<template lang="html">
<v-menu
v-model="menu"
:close-on-content-click="false"
>
<template #activator="{ on, attrs }">
<v-btn
v-bind="attrs"
icon
v-on="on"
>
<v-badge
:content="numFilters"
:value="numFilters"
color="primary"
overlap
>
<v-icon>mdi-magnify</v-icon>
</v-badge>
</v-btn>
</template>
<v-card>
<v-card-title>
Search
</v-card-title>
<v-card-text>
<v-select
v-model="typeFilterInput"
outlined
label="Type"
:items="filterOptions"
multiple
clearable
small-chips
deletable-chips
/>
<v-slide-x-transition group>
<div
v-for="(fieldFilter, index) in fieldFilters"
:key="index"
class="d-flex"
>
<v-text-field
v-model="fieldFilter.field"
class="text--mono"
label="Field"
outlined
/>
<v-text-field
v-model="fieldFilter.value"
label="Text"
class="ml-2"
outlined
/>
<v-btn
v-if="fieldFilters.length > 1"
icon
@click="fieldFilters.splice(index, 1)"
>
<v-icon>mdi-close</v-icon>
</v-btn>
</div>
</v-slide-x-transition>
<div
v-if="fieldFilters.length < 5"
class="d-flex"
>
<v-spacer />
<v-btn
icon
@click="fieldFilters.push({name: '', value: undefined})"
>
<v-icon>mdi-plus</v-icon>
</v-btn>
</div>
<v-card-actions>
<v-btn
text
@click="
fieldFilters = [{field: 'name', value: undefined}];
typeFilterInput = [];
menu = false;
"
>
<v-icon left>
mdi-close
</v-icon>
Clear
</v-btn>
<v-spacer />
<v-btn
text
color="primary"
@click="menu = false"
>
Find
</v-btn>
</v-card-actions>
</v-card-text>
</v-card>
</v-menu>
</template>
<script lang="js">
import PROPERTIES from '/imports/constants/PROPERTIES.js';
import escapeRegex from '/imports/api/utility/escapeRegex.js';
const filterOptions = [];
for (let key in PROPERTIES) {
if (key === 'reference') continue;
filterOptions.push({
text: PROPERTIES[key].name,
value: key,
});
}
export default {
props: {
value: {
type: Object,
default: undefined,
},
},
data(){return {
typeFilterInput: [],
fieldFilters: [{field: 'name', value: undefined}],
filterOptions,
menu: false,
}},
computed: {
filter() {
let filter = undefined;
if (this.typeFilterInput?.length) {
filter = filter || {};
filter.type = {$in: this.typeFilterInput};
}
this.fieldFilters?.forEach(fieldFilter => {
if (!fieldFilter.field || !fieldFilter.value) return;
const search = { $regex: escapeRegex(fieldFilter.value), '$options': 'i' };
filter = filter || {};
if (fieldFilter.field.includes('.')) {
// The user used dot notation, search exactly where they are looking
filter[fieldFilter.field] = search;
} else {
// No dot notation, search fields and their likely sub-fields
filter.$and = filter.$and || [];
filter.$and.push({
$or: [
{ [fieldFilter.field]: search },
{ [fieldFilter.field + '.calculation']: search },
{ [fieldFilter.field + '.text']: search },
],
});
}
});
return filter;
},
extraFields() {
let extraFields = [];
this.fieldFilters?.forEach(fieldFilter => {
if (!fieldFilter.field || !fieldFilter.value) return;
extraFields.push(fieldFilter.field);
});
return extraFields;
},
numFilters() {
let numFilters = 0;
if (this.typeFilterInput?.length) numFilters += 1;
numFilters += this.extraFields.length;
return numFilters;
}
},
watch: {
menu(val) {
if (!val) {
this.$emit('input', this.filter);
this.$emit('extra-fields-changed', this.extraFields);
}
}
},
}
</script>
<style lang="css" scoped>
</style>