filter is now generic component + fix

* fix select and combobox maxCharBtn
* Now filter is a component extending default fields.
For the moment, we have 2 types of filters: select and keyword.
We have default values that avoid filter ("all" for select and "" for keyword).
Filters are fields so we need to provide a "field" key with same structure as a Field.
We have to define "keys" that will be the keys the filter value will check.
We can set filter :"default" in order to filter the base keys of an object.
We can set filter :"settings" in order to filter settings, data must be an advanced template.
This commit is contained in:
Jordan Blasenhauer 2024-06-17 19:27:14 +02:00
parent cd274811b3
commit e7e0c2cfbc
6 changed files with 253 additions and 140 deletions

View file

@ -5,14 +5,12 @@ import Fields from "@components/Form/Fields.vue";
import Title from "@components/Widget/Title.vue";
import Subtitle from "@components/Widget/Subtitle.vue";
import Combobox from "@components/Forms/Field/Combobox.vue";
import Input from "@components/Forms/Field/Input.vue";
import Select from "@components/Forms/Field/Select.vue";
import Button from "@components/Widget/Button.vue";
import Text from "@components/Widget/Text.vue";
import Filter from "@components/Widget/Filter.vue";
import { v4 as uuidv4 } from "uuid";
import { plugin_types } from "@utils/variables";
import {
useFilter,
useCheckPluginsValidity,
useUpdateTempSettings,
useListenTemp,
@ -58,7 +56,11 @@ const props = defineProps({
required: true,
default: {},
},
filters: {
type: Object,
required: false,
default: {},
},
containerClass: {
type: String,
required: false,
@ -74,81 +76,108 @@ const props = defineProps({
const data = reactive({
currPlugin: "",
plugins: [],
keyword: "",
type: "all",
context: "all",
base: JSON.parse(JSON.stringify(props.template)),
isRegErr: false,
isReqErr: false,
settingErr: "",
pluginErr: "",
// Add filtering and check validity with regex and required
format: computed(() => {
// Check validity
setValidity();
// Deep copy
const template = JSON.parse(JSON.stringify(data.base));
// Add filter logic
const filterPlugin = [
{
type: "select",
value: data.type,
keys: ["type"],
},
];
const filterSettings = [
{
type: "keyword",
value: data.keyword,
keys: [
"id",
"label",
"name",
"description",
"help",
"value",
"setting_name",
],
},
{
type: "select",
value: data.context,
keys: ["context"],
},
];
// Start plugin filtering
const filterPlugins = useFilter(template, filterPlugin);
// Filter settings
filterPlugins.forEach((plugin, id) => {
// loop on plugin settings dict
const settings = [];
for (const [key, value] of Object.entries(plugin.settings)) {
// add to value the key as setting_name
settings.push({ ...value, setting_name: key });
}
const filterSettingsData = useFilter(settings, filterSettings);
// Transform list of dict by a dict of dict with setting_name as key and add update plugin settings
const settingsData = {};
filterSettingsData.forEach((setting) => {
settingsData[setting.setting_name] = setting;
});
filterPlugins[id].settings = settingsData;
});
// Case no settings found, remove plugin
const filterData = filterPlugins.filter((plugin) => {
return Object.keys(plugin.settings).length > 0;
});
data.plugins = getPluginNames(filterData);
data.currPlugin = getFirstPlugin(filterData);
return filterData;
}),
format: JSON.parse(JSON.stringify(props.template)),
});
const filters = [
{
filter: "default",
filterName: "type",
type: "select",
value: "all",
keys: ["type"],
field: {
id: uuidv4(),
value: "all",
// add 'all' as first value
values: ["all"].concat(plugin_types),
name: uuidv4(),
onlyDown: true,
label: "inp_select_plugin_type",
containerClass: "setting",
popovers: [
{
text: "inp_select_plugin_type_desc",
iconName: "info",
iconColor: "info",
},
],
columns: { pc: 3, tablet: 4, mobile: 12 },
},
},
{
filter: "settings",
filterName: "keyword",
type: "keyword",
value: "",
keys: [
"id",
"label",
"name",
"description",
"help",
"value",
"setting_name",
],
field: {
id: uuidv4(),
value: "",
type: "text",
name: uuidv4(),
containerClass: "setting",
label: "inp_search_settings",
placeholder: "inp_keyword",
isClipboard: false,
popovers: [
{
text: "inp_search_settings_desc",
iconName: "info",
iconColor: "info",
},
],
columns: { pc: 3, tablet: 4, mobile: 12 },
},
},
{
filter: "settings",
filterName: "context",
type: "select",
value: "all",
keys: ["context"],
field: {
id: uuidv4(),
value: "all",
// add 'all' as first value
values: ["all", "multisite", "global"],
name: uuidv4(),
onlyDown: true,
containerClass: "setting",
label: "inp_select_plugin_context",
popovers: [
{
text: "inp_select_plugin_context_desc",
iconName: "info",
iconColor: "info",
},
],
columns: { pc: 3, tablet: 4, mobile: 12 },
},
},
];
function filter(filterData) {
setValidity();
data.format = filterData;
data.plugins = getPluginNames(filterData);
data.currPlugin = getFirstPlugin(filterData);
}
function setValidity() {
const [isRegErr, isReqErr, settingErr, settingNameErr, pluginErr, id] =
useCheckPluginsValidity(data.base);
@ -202,59 +231,6 @@ const comboboxPlugin = {
columns: { pc: 3, tablet: 4, mobile: 12 },
};
const inpKeyword = {
id: uuidv4(),
value: "",
type: "text",
name: uuidv4(),
label: "inp_search_settings",
placeholder: "inp_keyword",
popovers: [
{
text: "inp_search_settings_desc",
iconName: "info",
iconColor: "info",
},
],
columns: { pc: 3, tablet: 4, mobile: 12 },
};
const selectType = {
id: uuidv4(),
value: "all",
// add 'all' as first value
values: ["all"].concat(plugin_types),
name: uuidv4(),
onlyDown: true,
label: "inp_select_plugin_type",
popovers: [
{
text: "inp_select_plugin_type_desc",
iconName: "info",
iconColor: "info",
},
],
columns: { pc: 3, tablet: 4, mobile: 12 },
};
const selectContext = {
id: uuidv4(),
value: "all",
// add 'all' as first value
values: ["all", "multisite", "global"],
name: uuidv4(),
onlyDown: true,
label: "inp_select_plugin_context",
popovers: [
{
text: "inp_select_plugin_context_desc",
iconName: "info",
iconColor: "info",
},
],
columns: { pc: 3, tablet: 4, mobile: 12 },
};
const buttonSave = {
id: uuidv4(),
text: "action_save",
@ -293,17 +269,13 @@ onUnmounted(() => {
>
<Title type="card" :title="'dashboard_advanced_mode'" />
<Subtitle type="card" :subtitle="'dashboard_advanced_mode_subtitle'" />
<Container :containerClass="`grid grid-cols-12 col-span-12 w-full`">
<Combobox
v-bind="comboboxPlugin"
:value="data.currPlugin"
:values="data.plugins"
@inp="data.currPlugin = $event"
/>
<Input @inp="(v) => (data.keyword = v)" v-bind="inpKeyword" />
<Select @inp="(v) => (data.type = v)" v-bind="selectType" />
<Select @inp="(v) => (data.context = v)" v-bind="selectContext" />
</Container>
<Combobox
v-bind="comboboxPlugin"
:value="data.currPlugin"
:values="data.plugins"
@inp="data.currPlugin = $event"
/>
<Filter @filter="(v) => filter(v)" :data="data.base" :filters="filters" />
<template v-for="plugin in data.format">
<Container
data-advanced-form-plugin

View file

@ -345,7 +345,7 @@ const emits = defineEmits(["inp"]);
<span :id="`${props.id}-text`" class="select-btn-name">
{{
(props.maxBtnChars && select.value) ||
props.value > props.maxBtnChars
props.value > +props.maxBtnChars
? `${
select.value.substring(0, props.maxBtnChars) ||
props.value.substring(0, props.maxBtnChars)

View file

@ -321,7 +321,7 @@ const emits = defineEmits(["inp"]);
<span :id="`${props.id}-text`" class="select-btn-name">
{{
(props.maxBtnChars && select.value) ||
props.value > props.maxBtnChars
props.value > +props.maxBtnChars
? `${
select.value.substring(0, props.maxBtnChars) ||
props.value.substring(0, props.maxBtnChars)

View file

@ -0,0 +1,142 @@
<script setup>
import { defineProps, reactive } from "vue";
import Container from "@components/Widget/Container.vue";
import Input from "@components/Forms/Field/Input.vue";
import Select from "@components/Forms/Field/Select.vue";
import { useFilter } from "@utils/form.js";
/**
@name Widget/Filter.vue
@description This component allow to filter any data object or array with a list of filters.
For the moment, we have 2 types of filters: select and keyword.
We have default values that avoid filter ("all" for select and "" for keyword).
Filters are fields so we need to provide a "field" key with same structure as a Field.
We have to define "keys" that will be the keys the filter value will check.
We can set filter :"default" in order to filter the base keys of an object.
We can set filter :"settings" in order to filter settings, data must be an advanced template.
Check example for more details.
@example
[
{
filter: "default", // or "settings"
type: "select",
value: "all",
keys: ["type"],
field: {
inpType: "select",
id: uuidv4(),
value: "all",
// add 'all' as first value
values: ["all"].concat(plugin_types),
name: uuidv4(),
onlyDown: true,
label: "inp_select_plugin_type",
popovers: [
{
text: "inp_select_plugin_type_desc",
iconName: "info",
iconColor: "info",
},
],
columns: { pc: 3, tablet: 4, mobile: 12 },
},
},
...
]
@param {object} filters - Fields with additional data to be used as filters.
@param {object|array} data - Data object or array to filter. Emit a filter event with the filtered data.
@param {string} containerClass - Additional class for the container.
*/
const props = defineProps({
filters: {
type: Object,
required: false,
default: {},
},
data: {
type: Object,
required: false,
default: {},
},
containerClass: {
type: String,
required: false,
default: "",
},
});
const emits = defineEmits(["filter"]);
const filters = reactive({
base: JSON.parse(JSON.stringify(props.filters)),
});
function filterData(filter, value) {
// Loop on filter.base and update the "value" key when matching filterName
filters.base.forEach((f) => {
if (f.filterName === filter.filterName) {
f.value = value;
}
});
// Start filtering
let template = JSON.parse(JSON.stringify(props.data));
const getFilters = JSON.parse(JSON.stringify(filters.base));
// Filter order for template : plugins, settings
// Base keys filtering (like plugin)
const defaultFilters = getFilters.filter((f) => f.filter === "default");
template = useFilter(template, defaultFilters);
// Specific settings filtering from advanced template
const filterSettings = getFilters.filter((f) => f.filter === "settings");
if (filterSettings.length) {
template.forEach((plugin, id) => {
// loop on plugin settings dict
const settings = [];
for (const [key, value] of Object.entries(plugin.settings)) {
// add to value the key as setting_name
settings.push({ ...value, setting_name: key });
}
const filterSettingsData = useFilter(settings, filterSettings);
// Transform list of dict by a dict of dict with setting_name as key and add update plugin settings
const settingsData = {};
filterSettingsData.forEach((setting) => {
settingsData[setting.setting_name] = setting;
});
template[id].settings = settingsData;
});
// Case no settings found, remove plugin
template = template.filter((plugin) => {
return Object.keys(plugin.settings).length > 0;
});
}
emits("filter", template);
}
</script>
<template>
<Container
v-if="filters.base"
:containerClass="`grid grid-cols-12 col-span-12 w-full`"
>
<template v-for="filter in filters.base">
<Input
v-if="filter.type === 'keyword'"
@inp="(v) => filterData(filter, v)"
v-bind="filter.field"
/>
<Select
v-if="filter.type === 'select'"
@inp="(v) => filterData(filter, v)"
v-bind="filter.field"
/>
</template>
</Container>
</template>

View file

@ -127,6 +127,7 @@ onUpdated(() => {
<template>
<Container :containerClass="`${props.containerClass} table-container`">
<slot></slot>
<Container
:containerClass="`${props.containerWrapClass} table-container-wrap`"
>

View file

@ -89,12 +89,11 @@ function useSubmitForm(data) {
}
];
@param {object} plugins - Object with the plugins data.
@param {object} filters - Object with the filters data.
@param {array} filters - Array with the filters data.
*/
function useFilter(items, filters) {
// loop on filters to determine types
filters = removeDefaultFilters(filters);
// Case no filters, return items
if (filters.length === 0) return items;
// loop on filters to determine types
@ -102,7 +101,6 @@ function useFilter(items, filters) {
filters.forEach((filter) => {
if (!filterTypes.includes(filter.type)) filterTypes.push(filter.type);
});
// Deepcopy
const data = JSON.parse(JSON.stringify(items));
const filterData = [];