mirror of
https://github.com/directus/directus.git
synced 2026-04-25 03:00:53 -04:00
improve related values display
This commit is contained in:
@@ -5,6 +5,7 @@ import adjustFieldsForDisplays from '@/utils/adjust-fields-for-displays';
|
||||
import getRelatedCollection from '@/utils/get-related-collection';
|
||||
import useCollection from '@/composables/use-collection';
|
||||
import { ref } from '@vue/composition-api';
|
||||
import options from './options.vue';
|
||||
|
||||
type Options = {
|
||||
template: string;
|
||||
@@ -16,19 +17,9 @@ export default defineDisplay(({ i18n }) => ({
|
||||
description: i18n.t('displays.related-values.description'),
|
||||
icon: 'settings_ethernet',
|
||||
handler: DisplayRelatedValues,
|
||||
options: [
|
||||
/** @todo make this a component so we have dynamic collection for display template component */
|
||||
{
|
||||
field: 'template',
|
||||
name: i18n.t('display_template'),
|
||||
type: 'string',
|
||||
meta: {
|
||||
interface: 'text-input',
|
||||
width: 'full',
|
||||
},
|
||||
},
|
||||
],
|
||||
options: options,
|
||||
types: ['alias', 'string', 'uuid', 'integer', 'bigInteger', 'json'],
|
||||
localTypes: ['m2m', 'm2o', 'o2m'],
|
||||
fields: (options: Options, { field, collection }) => {
|
||||
const relatedCollection = getRelatedCollection(collection, field);
|
||||
const { primaryKeyField } = useCollection(ref(relatedCollection as string));
|
||||
|
||||
82
app/src/displays/related-values/options.vue
Normal file
82
app/src/displays/related-values/options.vue
Normal file
@@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<v-notice type="warning" v-if="collection === null">
|
||||
{{ $t('interfaces.one-to-many.no_collection') }}
|
||||
</v-notice>
|
||||
<div v-else class="form-grid">
|
||||
<div class="field full">
|
||||
<p class="type-label">{{ $t('display_template') }}</p>
|
||||
<v-field-template :collection="relatedCollection" v-model="template" :depth="2" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Field } from '@/types';
|
||||
import { defineComponent, PropType, computed } from '@vue/composition-api';
|
||||
import { useRelationsStore } from '@/stores/';
|
||||
import { Relation } from '@/types/relations';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
value: {
|
||||
type: Object as PropType<any | null>,
|
||||
default: null,
|
||||
},
|
||||
fieldData: {
|
||||
type: Object as PropType<Field>,
|
||||
default: null,
|
||||
},
|
||||
relations: {
|
||||
type: Array as PropType<Relation[]>,
|
||||
default: () => [],
|
||||
},
|
||||
collection: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const relationsStore = useRelationsStore();
|
||||
const template = computed({
|
||||
get() {
|
||||
return props.value?.template;
|
||||
},
|
||||
set(newTemplate: string) {
|
||||
emit('input', {
|
||||
...(props.value || {}),
|
||||
template: newTemplate,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const relatedCollection = computed(() => {
|
||||
if (!props.fieldData || !props.relations || props.relations.length === 0) return null;
|
||||
const { field } = props.fieldData;
|
||||
const m2o = props.relations.find(
|
||||
(relation) => relation.many_collection === props.collection && relation.many_field === field
|
||||
);
|
||||
const o2m = props.relations.find(
|
||||
(relation) => relation.one_collection === props.collection && relation.one_field === field
|
||||
);
|
||||
|
||||
if (m2o !== undefined) {
|
||||
return m2o?.one_collection || null;
|
||||
}
|
||||
|
||||
if (o2m !== undefined) {
|
||||
return o2m?.many_collection || null;
|
||||
}
|
||||
});
|
||||
|
||||
return { template, relatedCollection };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/styles/mixins/form-grid';
|
||||
|
||||
.form-grid {
|
||||
@include form-grid;
|
||||
}
|
||||
</style>
|
||||
@@ -1,6 +1,6 @@
|
||||
import VueI18n from 'vue-i18n';
|
||||
import { Component } from 'vue';
|
||||
import { Field, types } from '@/types';
|
||||
import { Field, localTypes, types } from '@/types';
|
||||
|
||||
export type DisplayHandlerFunctionContext = {
|
||||
type: string;
|
||||
@@ -30,6 +30,7 @@ export type DisplayConfig = {
|
||||
handler: DisplayHandlerFunction | Component;
|
||||
options: null | DeepPartial<Field>[] | Component;
|
||||
types: readonly typeof types[number][];
|
||||
localTypes?: readonly typeof localTypes[number][];
|
||||
fields?: string[] | DisplayFieldsFunction;
|
||||
};
|
||||
|
||||
|
||||
@@ -22,13 +22,22 @@
|
||||
v-model="fieldData.meta.display_options"
|
||||
/>
|
||||
|
||||
<component v-model="fieldData" :is="`display-options-${selectedDisplay.id}`" v-else />
|
||||
<component
|
||||
v-model="fieldData.meta.display_options"
|
||||
:collection="collection"
|
||||
:field-data="fieldData"
|
||||
:relations="relations"
|
||||
:new-fields="newFields"
|
||||
:new-collections="newCollections"
|
||||
:is="`display-options-${selectedDisplay.id}`"
|
||||
v-else
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed } from '@vue/composition-api';
|
||||
import { defineComponent, computed, toRefs } from '@vue/composition-api';
|
||||
import { getDisplays } from '@/displays';
|
||||
import { getInterfaces } from '@/interfaces';
|
||||
import { FancySelectItem } from '@/components/v-fancy-select/types';
|
||||
@@ -42,6 +51,10 @@ export default defineComponent({
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
collection: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const displays = getDisplays();
|
||||
@@ -95,7 +108,9 @@ export default defineComponent({
|
||||
return displays.value.find((display) => display.id === state.fieldData.meta.display);
|
||||
});
|
||||
|
||||
return { fieldData: state.fieldData, selectItems, selectedDisplay };
|
||||
const { fieldData, relations, newCollections, newFields } = toRefs(state);
|
||||
|
||||
return { fieldData, selectItems, selectedDisplay, relations, newCollections, newFields };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -12,7 +12,7 @@ import { getInterfaces } from '@/interfaces';
|
||||
import { getDisplays } from '@/displays';
|
||||
import { InterfaceConfig } from '@/interfaces/types';
|
||||
import { DisplayConfig } from '@/displays/types';
|
||||
import { Field } from '@/types';
|
||||
import { Field, localTypes } from '@/types';
|
||||
import Vue from 'vue';
|
||||
|
||||
const fieldsStore = useFieldsStore();
|
||||
@@ -25,11 +25,7 @@ let availableDisplays: ComputedRef<DisplayConfig[]>;
|
||||
|
||||
export { state, availableInterfaces, availableDisplays, initLocalStore, clearLocalStore };
|
||||
|
||||
function initLocalStore(
|
||||
collection: string,
|
||||
field: string,
|
||||
type: 'standard' | 'file' | 'files' | 'm2o' | 'o2m' | 'm2m' | 'presentation' | 'translations'
|
||||
) {
|
||||
function initLocalStore(collection: string, field: string, type: typeof localTypes[number]) {
|
||||
const interfaces = getInterfaces();
|
||||
const displays = getDisplays();
|
||||
|
||||
@@ -91,8 +87,8 @@ function initLocalStore(
|
||||
availableDisplays = computed(() =>
|
||||
displays.value.filter((display) => {
|
||||
const matchesType = display.types.includes(state.fieldData?.type || 'alias');
|
||||
const matchesRelation = true;
|
||||
return matchesType && matchesRelation;
|
||||
let matchesLocalType = display.localTypes?.includes(type);
|
||||
return matchesType && (matchesLocalType === undefined || matchesLocalType);
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ export const types = [
|
||||
'unknown',
|
||||
] as const;
|
||||
|
||||
export const localTypes = ['standard', 'file', 'files', 'm2o', 'o2m', 'm2m', 'presentation', 'translations'] as const;
|
||||
|
||||
export type FieldSchema = {
|
||||
/** @todo import this from knex-schema-inspector when that's launched */
|
||||
name: string;
|
||||
@@ -70,7 +72,6 @@ export interface FieldRaw {
|
||||
collection: string;
|
||||
field: string;
|
||||
type: typeof types[number];
|
||||
|
||||
schema: FieldSchema | null;
|
||||
meta: FieldMeta | null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user