Fix ability to change M2O field type and prevent incompatible interfaces in advanced mode (#10096)

* Fix ability to change M2O field type and prevent incompatible interfaces in advanced mode

* Removed unused imports

* Rename interface variable
This commit is contained in:
Oreille
2021-12-01 22:16:37 +01:00
committed by GitHub
parent fc0a0a672c
commit 9c9e2eff5b
19 changed files with 75 additions and 101 deletions

View File

@@ -15,11 +15,10 @@
<script lang="ts">
import { useI18n } from 'vue-i18n';
import { defineComponent, computed } from 'vue';
import { getDisplays } from '@/displays';
import { getInterfaces } from '@/interfaces';
import { getDisplay } from '@/displays';
import { getInterface } from '@/interfaces';
import { FancySelectItem } from '@/components/v-fancy-select/types';
import { clone } from 'lodash';
import { InterfaceConfig, DisplayConfig } from '@directus/shared/types';
import { useFieldDetailStore, syncFieldDetailStoreProperty } from '../store';
import { storeToRefs } from 'pinia';
import ExtensionOptions from '../shared/extension-options.vue';
@@ -36,12 +35,7 @@ export default defineComponent({
const interfaceID = computed(() => field.value.meta?.interface);
const display = syncFieldDetailStoreProperty('field.meta.display');
const { displays } = getDisplays();
const { interfaces } = getInterfaces();
const selectedInterface = computed(() => {
return interfaces.value.find((inter: InterfaceConfig) => inter.id === interfaceID.value);
});
const selectedInterface = computed(() => getInterface(interfaceID.value));
const selectItems = computed(() => {
let recommended = clone(selectedInterface.value?.recommendedDisplays) || [];
@@ -83,9 +77,7 @@ export default defineComponent({
return recommendedItems;
});
const selectedDisplay = computed(() => {
return displays.value.find((displayConfig: DisplayConfig) => displayConfig.id === display.value);
});
const selectedDisplay = computed(() => getDisplay(display.value));
return { t, selectItems, selectedDisplay, display };
},

View File

@@ -20,9 +20,8 @@
<script lang="ts">
import { useI18n } from 'vue-i18n';
import { defineComponent, computed } from 'vue';
import { getInterfaces } from '@/interfaces';
import { getInterface } from '@/interfaces';
import { FancySelectItem } from '@/components/v-fancy-select/types';
import { InterfaceConfig } from '@directus/shared/types';
import { useFieldDetailStore, syncFieldDetailStoreProperty } from '../store/';
import { storeToRefs } from 'pinia';
import ExtensionOptions from '../shared/extension-options.vue';
@@ -40,8 +39,6 @@ export default defineComponent({
const { field, interfacesForType } = storeToRefs(fieldDetailStore);
const type = computed(() => field.value.type);
const { interfaces } = getInterfaces();
const selectItems = computed(() => {
const recommendedInterfacesPerType: { [type: string]: string[] } = {
string: ['input', 'select-dropdown'],
@@ -96,9 +93,7 @@ export default defineComponent({
return recommendedItems;
});
const selectedInterface = computed(() => {
return interfaces.value.find((inter: InterfaceConfig) => inter.id === interfaceID.value);
});
const selectedInterface = computed(() => getInterface(interfaceID.value));
return { t, selectItems, selectedInterface, interfaceID, options };
},

View File

@@ -260,9 +260,7 @@ export default defineComponent({
const typesWithLabels = computed(() => translate(fieldTypes));
const typeDisabled = computed(() => {
return ['file', 'files', 'o2m', 'm2m', 'm2a', 'm2o', 'translations'].includes(localType.value);
});
const typeDisabled = computed(() => localType.value !== 'standard');
const typePlaceholder = computed(() => {
if (localType.value === 'm2o') {

View File

@@ -16,7 +16,7 @@
{{ t('type') }}
</div>
<v-select v-model="type" :items="typeOptions" :disabled="typeOptions.length === 1" />
<v-select v-model="type" :items="typeOptions" :disabled="typeDisabled" />
</div>
<div class="field half-left">
@@ -56,7 +56,7 @@
<script lang="ts">
import { defineComponent, computed } from 'vue';
import { getInterfaces } from '@/interfaces';
import { getInterface } from '@/interfaces';
import { useI18n } from 'vue-i18n';
import { useFieldDetailStore, syncFieldDetailStoreProperty } from '../store/';
import { storeToRefs } from 'pinia';
@@ -82,9 +82,7 @@ export default defineComponent({
const { readyToSave, saving, localType, collection } = storeToRefs(fieldDetail);
const { t } = useI18n();
const { interfaces } = getInterfaces();
const chosenInterface = computed(() => interfaces.value.find((inter) => inter.id === props.chosenInterface));
const chosenInterface = computed(() => getInterface(props.chosenInterface));
const typeOptions = computed(() => {
if (!chosenInterface.value) return [];
@@ -95,6 +93,8 @@ export default defineComponent({
}));
});
const typeDisabled = computed(() => typeOptions.value.length === 1 || localType.value !== 'standard');
const key = syncFieldDetailStoreProperty('field.field');
const type = syncFieldDetailStoreProperty('field.type');
const defaultValue = syncFieldDetailStoreProperty('field.schema.default_value');
@@ -105,6 +105,7 @@ export default defineComponent({
key,
t,
type,
typeDisabled,
typeOptions,
defaultValue,
required,

View File

@@ -23,8 +23,8 @@
<script lang="ts">
import { defineComponent, PropType, computed } from 'vue';
import { getInterfaces } from '@/interfaces';
import { getDisplays } from '@/displays';
import { getInterface } from '@/interfaces';
import { getDisplay } from '@/displays';
import { useFieldDetailStore } from '../store/';
import { get } from 'lodash';
import { storeToRefs } from 'pinia';
@@ -53,19 +53,15 @@ export default defineComponent({
const { collection, field, relations, fields, collections } = storeToRefs(fieldDetail);
const { interfaces } = getInterfaces();
const { displays } = getDisplays();
const extensionInfo = computed(() => {
if (props.type === 'interface') {
return interfaces.value.find((inter) => inter.id === props.extension);
switch (props.type) {
case 'interface':
return getInterface(props.extension);
case 'display':
return getDisplay(props.extension);
default:
return null;
}
if (props.type === 'display') {
return displays.value.find((display) => display.id === props.extension);
}
return null;
});
const usesCustomComponent = computed(() => {

View File

@@ -1,6 +1,6 @@
import { set } from 'lodash';
import { State, StateUpdates } from '../types';
import { getInterfaces } from '@/interfaces';
import { getInterface } from '@/interfaces';
/**
* In case a relational field removed the schema object, we'll have to make sure it's re-added
@@ -20,7 +20,7 @@ export function resetSchema(updates: StateUpdates, state: State) {
export function setLocalTypeForInterface(updates: StateUpdates) {
if (!updates.field?.meta?.interface) return;
const chosenInterface = getInterfaces().interfaces.value.find((inter) => inter.id === updates.field!.meta!.interface);
const chosenInterface = getInterface(updates.field.meta.interface);
if (!chosenInterface) return;
@@ -36,7 +36,7 @@ export function setLocalTypeForInterface(updates: StateUpdates) {
export function setTypeForInterface(updates: StateUpdates, state: State) {
if (!updates.field?.meta?.interface) return;
const chosenInterface = getInterfaces().interfaces.value.find((inter) => inter.id === updates.field!.meta!.interface);
const chosenInterface = getInterface(updates.field.meta.interface);
if (!chosenInterface) return updates;

View File

@@ -1,4 +1,5 @@
import { HelperFunctions, State, StateUpdates } from '../types';
import { getInterface } from '@/interfaces';
import { set } from 'lodash';
export function applyChanges(updates: StateUpdates, _state: State, helperFn: HelperFunctions) {
@@ -6,10 +7,11 @@ export function applyChanges(updates: StateUpdates, _state: State, helperFn: Hel
if (hasChanged('field.type')) {
setSpecialForType(updates);
updateInterface(updates, helperFn);
}
}
export function setSpecialForType(updates: StateUpdates) {
function setSpecialForType(updates: StateUpdates) {
const type = updates.field?.type;
switch (type) {
case 'uuid':
@@ -26,3 +28,11 @@ export function setSpecialForType(updates: StateUpdates) {
set(updates, 'field.meta.special', null);
}
}
function updateInterface(updates: StateUpdates, fn: HelperFunctions) {
const interface_ = getInterface(fn.getCurrent('field.meta.interface'));
const type = updates.field?.type;
if (type && !interface_?.types.includes(type)) {
set(updates, 'field.meta.interface', undefined);
}
}

View File

@@ -141,13 +141,13 @@
import { useI18n } from 'vue-i18n';
import { defineComponent, PropType, ref, computed } from 'vue';
import { useCollectionsStore, useFieldsStore } from '@/stores/';
import { getInterfaces } from '@/interfaces';
import { getInterface } from '@/interfaces';
import { useRouter } from 'vue-router';
import { cloneDeep } from 'lodash';
import { getLocalTypeForField } from '../../get-local-type';
import { notify } from '@/utils/notify';
import { unexpectedError } from '@/utils/unexpected-error';
import { Field, InterfaceConfig } from '@directus/shared/types';
import { Field } from '@directus/shared/types';
import FieldSelectMenu from './field-select-menu.vue';
import hideDragImage from '@/utils/hide-drag-image';
import Draggable from 'vuedraggable';
@@ -178,16 +178,13 @@ export default defineComponent({
const collectionsStore = useCollectionsStore();
const fieldsStore = useFieldsStore();
const { interfaces } = getInterfaces();
const editActive = ref(false);
const { deleteActive, deleting, deleteField } = useDeleteField();
const { duplicateActive, duplicateName, collections, duplicateTo, saveDuplicate, duplicating } = useDuplicate();
const interfaceName = computed(() => {
return interfaces.value.find((inter: InterfaceConfig) => inter.id === props.field.meta?.interface)?.name;
});
const interfaceName = computed(() => getInterface(props.field.meta?.interface)?.name);
const hidden = computed(() => props.field.meta?.hidden === true);

View File

@@ -92,7 +92,7 @@ import SettingsNavigation from '../../../components/navigation.vue';
import api from '@/api';
import { Header } from '@/components/v-table/types';
import { useCollectionsStore } from '@/stores/';
import { getLayouts } from '@/layouts';
import { getLayout } from '@/layouts';
import { useRouter } from 'vue-router';
import ValueNull from '@/views/private/components/value-null';
import PresetsInfoSidebarDetail from './components/presets-info-sidebar-detail.vue';
@@ -123,7 +123,6 @@ export default defineComponent({
const router = useRouter();
const { layouts } = getLayouts();
const collectionsStore = useCollectionsStore();
const selection = ref<Preset[]>([]);
@@ -175,7 +174,7 @@ export default defineComponent({
}
const collection = collectionsStore.getCollection(preset.collection)?.name;
const layout = layouts.value.find((l) => l.id === preset.layout)?.name;
const layout = getLayout(preset.layout)?.name;
return {
id: preset.id,