mirror of
https://github.com/directus/directus.git
synced 2026-01-25 22:18:25 -05:00
script[setup]: interfaces/group-accordion (#18409)
This commit is contained in:
@@ -33,136 +33,97 @@
|
||||
</v-item>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType, computed } from 'vue';
|
||||
import { merge, isNil } from 'lodash';
|
||||
import { Field } from '@directus/types';
|
||||
import { ValidationError } from '@directus/types';
|
||||
<script setup lang="ts">
|
||||
import { Field, ValidationError } from '@directus/types';
|
||||
import { isNil, merge } from 'lodash';
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'AccordionSection',
|
||||
props: {
|
||||
field: {
|
||||
type: Object as PropType<Field>,
|
||||
required: true,
|
||||
},
|
||||
fields: {
|
||||
type: Array as PropType<Field[]>,
|
||||
required: true,
|
||||
},
|
||||
values: {
|
||||
type: Object as PropType<Record<string, unknown>>,
|
||||
required: true,
|
||||
},
|
||||
initialValues: {
|
||||
type: Object as PropType<Record<string, unknown>>,
|
||||
required: true,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
batchMode: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
batchActiveFields: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => [],
|
||||
},
|
||||
primaryKey: {
|
||||
type: [Number, String],
|
||||
required: true,
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
validationErrors: {
|
||||
type: Array as PropType<ValidationError[]>,
|
||||
default: () => [],
|
||||
},
|
||||
badge: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
group: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
direction: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
emits: ['apply', 'toggleAll'],
|
||||
setup(props, { emit }) {
|
||||
const { t } = useI18n();
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
field: Field;
|
||||
fields: Field[];
|
||||
values: Record<string, unknown>;
|
||||
initialValues: Record<string, unknown>;
|
||||
disabled?: boolean;
|
||||
batchMode?: boolean;
|
||||
batchActiveFields?: string[];
|
||||
primaryKey: number | string;
|
||||
loading?: boolean;
|
||||
validationErrors?: ValidationError[];
|
||||
badge?: string;
|
||||
group: string;
|
||||
multiple?: boolean;
|
||||
direction?: string;
|
||||
}>(),
|
||||
{
|
||||
batchActiveFields: () => [],
|
||||
validationErrors: () => [],
|
||||
}
|
||||
);
|
||||
|
||||
const fieldsInSection = computed(() => {
|
||||
let fields: Field[] = [merge({}, props.field, { hideLabel: true })];
|
||||
const emit = defineEmits<{
|
||||
(e: 'apply', value: Record<string, unknown>): void;
|
||||
(e: 'toggleAll'): void;
|
||||
}>();
|
||||
|
||||
if (props.field.meta?.special?.includes('group')) {
|
||||
fields.push(...getFieldsForGroup(props.field.meta?.field));
|
||||
}
|
||||
const { t } = useI18n();
|
||||
|
||||
return fields;
|
||||
});
|
||||
const fieldsInSection = computed(() => {
|
||||
let fields: Field[] = [merge({}, props.field, { hideLabel: true })];
|
||||
|
||||
const edited = computed(() => {
|
||||
if (!props.values) return false;
|
||||
if (props.field.meta?.special?.includes('group')) {
|
||||
fields.push(...getFieldsForGroup(props.field.meta?.field));
|
||||
}
|
||||
|
||||
const editedFields = Object.keys(props.values);
|
||||
return fieldsInSection.value.some((field) => editedFields.includes(field.field));
|
||||
});
|
||||
|
||||
const validationMessage = computed(() => {
|
||||
const validationError = props.validationErrors.find((error) => error.field === props.field.field);
|
||||
if (validationError === undefined) return;
|
||||
|
||||
if (validationError.code === 'RECORD_NOT_UNIQUE') {
|
||||
return t('validationError.unique');
|
||||
} else {
|
||||
return t(`validationError.${validationError.type}`, validationError);
|
||||
}
|
||||
});
|
||||
|
||||
return { t, fieldsInSection, edited, handleModifier, validationMessage };
|
||||
|
||||
function handleModifier(event: MouseEvent, toggle: () => void) {
|
||||
if (props.multiple === false) {
|
||||
toggle();
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.shiftKey) {
|
||||
emit('toggleAll');
|
||||
} else {
|
||||
toggle();
|
||||
}
|
||||
}
|
||||
|
||||
function getFieldsForGroup(group: null | string, passed: string[] = []): Field[] {
|
||||
const fieldsInGroup: Field[] = props.fields.filter((field) => {
|
||||
return field.meta?.group === group || (group === null && isNil(field.meta));
|
||||
});
|
||||
|
||||
for (const field of fieldsInGroup) {
|
||||
if (field.meta?.special?.includes('group') && !passed.includes(field.meta!.field)) {
|
||||
passed.push(field.meta!.field);
|
||||
fieldsInGroup.push(...getFieldsForGroup(field.meta!.field, passed));
|
||||
}
|
||||
}
|
||||
|
||||
return fieldsInGroup;
|
||||
}
|
||||
},
|
||||
return fields;
|
||||
});
|
||||
|
||||
const edited = computed(() => {
|
||||
if (!props.values) return false;
|
||||
|
||||
const editedFields = Object.keys(props.values);
|
||||
return fieldsInSection.value.some((field) => editedFields.includes(field.field));
|
||||
});
|
||||
|
||||
const validationMessage = computed(() => {
|
||||
const validationError = props.validationErrors.find((error) => error.field === props.field.field);
|
||||
if (validationError === undefined) return;
|
||||
|
||||
if (validationError.code === 'RECORD_NOT_UNIQUE') {
|
||||
return t('validationError.unique');
|
||||
} else {
|
||||
return t(`validationError.${validationError.type}`, validationError);
|
||||
}
|
||||
});
|
||||
|
||||
function handleModifier(event: MouseEvent, toggle: () => void) {
|
||||
if (props.multiple === false) {
|
||||
toggle();
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.shiftKey) {
|
||||
emit('toggleAll');
|
||||
} else {
|
||||
toggle();
|
||||
}
|
||||
}
|
||||
|
||||
function getFieldsForGroup(group: null | string, passed: string[] = []): Field[] {
|
||||
const fieldsInGroup: Field[] = props.fields.filter((field) => {
|
||||
return field.meta?.group === group || (group === null && isNil(field.meta));
|
||||
});
|
||||
|
||||
for (const field of fieldsInGroup) {
|
||||
if (field.meta?.special?.includes('group') && !passed.includes(field.meta!.field)) {
|
||||
passed.push(field.meta!.field);
|
||||
fieldsInGroup.push(...getFieldsForGroup(field.meta!.field, passed));
|
||||
}
|
||||
}
|
||||
|
||||
return fieldsInGroup;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -24,154 +24,111 @@
|
||||
</v-item-group>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Field } from '@directus/types';
|
||||
import { defineComponent, PropType, ref, watch } from 'vue';
|
||||
import { ValidationError } from '@directus/types';
|
||||
import AccordionSection from './accordion-section.vue';
|
||||
<script setup lang="ts">
|
||||
import { Field, ValidationError } from '@directus/types';
|
||||
import { isEqual } from 'lodash';
|
||||
import { ref, watch } from 'vue';
|
||||
import AccordionSection from './accordion-section.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'InterfaceGroupAccordion',
|
||||
components: { AccordionSection },
|
||||
props: {
|
||||
field: {
|
||||
type: Object as PropType<Field>,
|
||||
required: true,
|
||||
},
|
||||
fields: {
|
||||
type: Array as PropType<Field[]>,
|
||||
required: true,
|
||||
},
|
||||
values: {
|
||||
type: Object as PropType<Record<string, unknown>>,
|
||||
required: true,
|
||||
},
|
||||
initialValues: {
|
||||
type: Object as PropType<Record<string, unknown>>,
|
||||
required: true,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
batchMode: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
batchActiveFields: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => [],
|
||||
},
|
||||
primaryKey: {
|
||||
type: [Number, String],
|
||||
required: true,
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
validationErrors: {
|
||||
type: Array as PropType<ValidationError[]>,
|
||||
default: () => [],
|
||||
},
|
||||
badge: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
rawEditorEnabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
accordionMode: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
start: {
|
||||
type: String,
|
||||
enum: ['opened', 'closed', 'first'],
|
||||
default: 'closed',
|
||||
},
|
||||
direction: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
emits: ['apply'],
|
||||
setup(props) {
|
||||
const selection = ref<string[]>([]);
|
||||
const { groupFields, groupValues } = useComputedGroup();
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
field: Field;
|
||||
fields: Field[];
|
||||
values: Record<string, unknown>;
|
||||
initialValues: Record<string, unknown>;
|
||||
disabled?: boolean;
|
||||
batchMode?: boolean;
|
||||
batchActiveFields?: string[];
|
||||
primaryKey: string | number;
|
||||
loading?: boolean;
|
||||
validationErrors?: ValidationError[];
|
||||
badge?: string;
|
||||
rawEditorEnabled?: boolean;
|
||||
accordionMode?: boolean;
|
||||
start?: 'opened' | 'closed' | 'first';
|
||||
direction?: string;
|
||||
}>(),
|
||||
{
|
||||
batchActiveFields: () => [],
|
||||
validationErrors: () => [],
|
||||
accordionMode: true,
|
||||
start: 'closed',
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.start,
|
||||
(start) => {
|
||||
if (start === 'opened') {
|
||||
selection.value = groupFields.value.map((field) => field.field);
|
||||
}
|
||||
defineEmits<{
|
||||
(e: 'apply', value: Record<string, unknown>): void;
|
||||
}>();
|
||||
|
||||
if (start === 'first') {
|
||||
selection.value = [groupFields.value[0].field];
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
const selection = ref<string[]>([]);
|
||||
const { groupFields, groupValues } = useComputedGroup();
|
||||
|
||||
watch(
|
||||
() => props.validationErrors,
|
||||
(newVal, oldVal) => {
|
||||
if (!props.validationErrors) return;
|
||||
if (isEqual(newVal, oldVal)) return;
|
||||
|
||||
const includedFieldsWithErrors = props.validationErrors.filter((validationError) =>
|
||||
groupFields.value.find((rootField) => rootField.field === validationError.field)
|
||||
);
|
||||
|
||||
if (includedFieldsWithErrors.length > 0) selection.value = [includedFieldsWithErrors[0].field];
|
||||
}
|
||||
);
|
||||
|
||||
return { groupFields, groupValues, selection, toggleAll };
|
||||
|
||||
function toggleAll() {
|
||||
if (props.accordionMode === true) return;
|
||||
|
||||
if (selection.value.length === groupFields.value.length) {
|
||||
selection.value = [];
|
||||
} else {
|
||||
selection.value = groupFields.value.map((field) => field.field);
|
||||
}
|
||||
watch(
|
||||
() => props.start,
|
||||
(start) => {
|
||||
if (start === 'opened') {
|
||||
selection.value = groupFields.value.map((field) => field.field);
|
||||
}
|
||||
|
||||
function useComputedGroup() {
|
||||
const groupFields = ref<Field[]>(limitFields());
|
||||
const groupValues = ref<Record<string, any>>({});
|
||||
|
||||
watch(
|
||||
() => props.fields,
|
||||
() => {
|
||||
const newVal = limitFields();
|
||||
|
||||
if (!isEqual(groupFields.value, newVal)) {
|
||||
groupFields.value = newVal;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.values,
|
||||
(newVal) => {
|
||||
if (!isEqual(groupValues.value, newVal)) {
|
||||
groupValues.value = newVal;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return { groupFields, groupValues };
|
||||
|
||||
function limitFields(): Field[] {
|
||||
return props.fields.filter((field) => field.meta?.group === props.field.meta?.field);
|
||||
}
|
||||
if (start === 'first') {
|
||||
selection.value = [groupFields.value[0].field];
|
||||
}
|
||||
},
|
||||
});
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.validationErrors,
|
||||
(newVal, oldVal) => {
|
||||
if (!props.validationErrors) return;
|
||||
if (isEqual(newVal, oldVal)) return;
|
||||
|
||||
const includedFieldsWithErrors = props.validationErrors.filter((validationError) =>
|
||||
groupFields.value.find((rootField) => rootField.field === validationError.field)
|
||||
);
|
||||
|
||||
if (includedFieldsWithErrors.length > 0) selection.value = [includedFieldsWithErrors[0].field];
|
||||
}
|
||||
);
|
||||
|
||||
function toggleAll() {
|
||||
if (props.accordionMode === true) return;
|
||||
|
||||
if (selection.value.length === groupFields.value.length) {
|
||||
selection.value = [];
|
||||
} else {
|
||||
selection.value = groupFields.value.map((field) => field.field);
|
||||
}
|
||||
}
|
||||
|
||||
function useComputedGroup() {
|
||||
const groupFields = ref<Field[]>(limitFields());
|
||||
const groupValues = ref<Record<string, any>>({});
|
||||
|
||||
watch(
|
||||
() => props.fields,
|
||||
() => {
|
||||
const newVal = limitFields();
|
||||
|
||||
if (!isEqual(groupFields.value, newVal)) {
|
||||
groupFields.value = newVal;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.values,
|
||||
(newVal) => {
|
||||
if (!isEqual(groupValues.value, newVal)) {
|
||||
groupValues.value = newVal;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return { groupFields, groupValues };
|
||||
|
||||
function limitFields(): Field[] {
|
||||
return props.fields.filter((field) => field.meta?.group === props.field.meta?.field);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user