mirror of
https://github.com/directus/directus.git
synced 2026-02-05 18:25:02 -05:00
Add support for Conditional Fields (#6864)
* Add conditions field to directus_fields * Add conditions configuration * Apply conditional overrides * Handle conditions in nested groups * Fix reverse mutating conditions * Start on filter setup interface * Move field types/constants to shared * [WIP] Updated client side filter validation * Support logical operators in client validation step * Use new validation util in conditions check * Add nesting in filter seutp * Add filter rule setup configurator * Fixes that should've been done in the merge * Strip out filter-settings interface TBD in a new PR * Move browser to index
This commit is contained in:
@@ -1,9 +1,5 @@
|
||||
<template>
|
||||
<div
|
||||
:key="field.field"
|
||||
class="field"
|
||||
:class="[(field.meta && field.meta.width) || 'full', { invalid: validationError }]"
|
||||
>
|
||||
<div :key="field.field" class="field" :class="[field.meta?.width || 'full', { invalid: validationError }]">
|
||||
<v-menu v-if="field.hideLabel !== true" placement="bottom-start" show-arrow :disabled="isDisabled">
|
||||
<template #activator="{ toggle, active }">
|
||||
<form-field-label
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
<component
|
||||
:is="`interface-${field.meta?.interface || 'group-raw'}`"
|
||||
v-if="field.meta?.special?.includes('group')"
|
||||
v-show="!field.meta?.hidden"
|
||||
:key="field.field"
|
||||
:class="field.meta?.width || 'full'"
|
||||
:field="field"
|
||||
@@ -39,6 +40,7 @@
|
||||
|
||||
<form-field
|
||||
v-else
|
||||
v-show="!field.meta?.hidden"
|
||||
:key="field.field"
|
||||
:field="field"
|
||||
:autofocus="index === firstEditableFieldIndex && autofocus"
|
||||
@@ -62,12 +64,12 @@
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { defineComponent, PropType, computed, ref, provide } from 'vue';
|
||||
import { useFieldsStore } from '@/stores/';
|
||||
import { Field, FieldRaw } from '@directus/shared/types';
|
||||
import { Field, FieldRaw, ValidationError } from '@directus/shared/types';
|
||||
import { clone, cloneDeep, isNil, merge, omit } from 'lodash';
|
||||
import useFormFields from '@/composables/use-form-fields';
|
||||
import { ValidationError } from '@/types';
|
||||
import { useElementSize } from '@/composables/use-element-size';
|
||||
import FormField from './form-field.vue';
|
||||
import { validatePayload } from '@directus/shared/utils';
|
||||
|
||||
type FieldValues = {
|
||||
[field: string]: any;
|
||||
@@ -202,16 +204,10 @@ export default defineComponent({
|
||||
throw new Error('[v-form]: You need to pass either the collection or fields prop.');
|
||||
});
|
||||
|
||||
const fieldsInGroup = computed(() =>
|
||||
fields.value.filter(
|
||||
(field) => field.meta?.group === props.group || (props.group === null && isNil(field.meta?.group))
|
||||
)
|
||||
);
|
||||
const fieldsParsed = computed(() => {
|
||||
if (props.group !== null) return fields.value;
|
||||
|
||||
const { formFields } = useFormFields(fieldsInGroup);
|
||||
|
||||
const formFieldsParsed = computed(() => {
|
||||
const blockPrimaryKey = (field: Field) => {
|
||||
const setPrimaryKeyReadonly = (field: Field) => {
|
||||
if (
|
||||
field.schema?.has_auto_increment === true ||
|
||||
(field.schema?.is_primary_key === true && props.primaryKey !== '+')
|
||||
@@ -225,10 +221,44 @@ export default defineComponent({
|
||||
return field;
|
||||
};
|
||||
|
||||
return formFields.value.map((field) => blockPrimaryKey(field));
|
||||
const applyConditions = (field: Field) => {
|
||||
if (field.meta && Array.isArray(field.meta?.conditions)) {
|
||||
const conditions = [...field.meta.conditions].reverse();
|
||||
|
||||
const matchingCondition = conditions.find((condition) => {
|
||||
const errors = validatePayload(condition.rule, values.value, { requireAll: true });
|
||||
return errors.length === 0;
|
||||
});
|
||||
|
||||
if (matchingCondition) {
|
||||
return {
|
||||
...field,
|
||||
meta: merge({}, field.meta || {}, {
|
||||
readonly: matchingCondition.readonly,
|
||||
options: matchingCondition.options,
|
||||
hidden: matchingCondition.hidden,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
return field;
|
||||
} else {
|
||||
return field;
|
||||
}
|
||||
};
|
||||
|
||||
return fields.value.map((field) => setPrimaryKeyReadonly(field)).map((field) => applyConditions(field));
|
||||
});
|
||||
|
||||
return { formFields: formFieldsParsed, isDisabled, getFieldsForGroup };
|
||||
const fieldsInGroup = computed(() =>
|
||||
fieldsParsed.value.filter(
|
||||
(field) => field.meta?.group === props.group || (props.group === null && isNil(field.meta?.group))
|
||||
)
|
||||
);
|
||||
|
||||
const { formFields } = useFormFields(fieldsInGroup);
|
||||
|
||||
return { formFields, isDisabled, getFieldsForGroup };
|
||||
|
||||
function isDisabled(field: Field) {
|
||||
return (
|
||||
@@ -240,7 +270,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
function getFieldsForGroup(group: null | number): Field[] {
|
||||
const fieldsInGroup: Field[] = fields.value.filter(
|
||||
const fieldsInGroup: Field[] = fieldsParsed.value.filter(
|
||||
(field) => field.meta?.group === group || (group === null && isNil(field.meta))
|
||||
);
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
ref="input"
|
||||
v-focus="autofocus"
|
||||
v-bind="attributes"
|
||||
:placeholder="placeholder"
|
||||
:autocomplete="autocomplete"
|
||||
:type="type"
|
||||
:min="min"
|
||||
@@ -84,6 +85,10 @@ export default defineComponent({
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
modelValue: {
|
||||
type: [String, Number],
|
||||
default: null,
|
||||
|
||||
Reference in New Issue
Block a user