mirror of
https://github.com/directus/directus.git
synced 2026-02-03 20:55:11 -05:00
Interface grouping (#717)
* Manage types * Fix typing * Unset interface on local type change * Add note * Limit available displays based on localType * Filter displays based on localtype * Only show displays that fit the type * Limit type options for interface selection * Dont import unused type
This commit is contained in:
@@ -325,7 +325,7 @@ export default defineComponent({
|
||||
|
||||
if (systemFields[2].enabled === true) {
|
||||
fields.push({
|
||||
type: 'owner',
|
||||
type: 'user_created',
|
||||
datatype: 'INT',
|
||||
field: systemFields[2].name,
|
||||
interface: 'owner',
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
import { defineComponent, computed, PropType } from '@vue/composition-api';
|
||||
import i18n from '@/lang';
|
||||
import { FormField } from '@/components/v-form/types';
|
||||
import { Field } from '@/stores/fields/types';
|
||||
import { Field, types } from '@/stores/fields/types';
|
||||
import interfaces from '@/interfaces';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@@ -24,6 +25,21 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const selectedInterface = computed(() => interfaces.find((inter) => inter.id === props.value.interface));
|
||||
|
||||
const typeChoices = computed(() => {
|
||||
let availableTypes = types;
|
||||
|
||||
if (selectedInterface.value) {
|
||||
availableTypes = selectedInterface.value.types;
|
||||
}
|
||||
|
||||
return availableTypes.map((type) => ({
|
||||
text: i18n.t(type),
|
||||
value: type,
|
||||
}));
|
||||
});
|
||||
|
||||
const fields = computed(() => {
|
||||
const fields: FormField[] = [
|
||||
{
|
||||
@@ -127,8 +143,11 @@ export default defineComponent({
|
||||
{
|
||||
field: 'type',
|
||||
name: i18n.t('directus_type'),
|
||||
interface: 'text-input',
|
||||
interface: 'dropdown',
|
||||
width: 'half',
|
||||
options: {
|
||||
choices: typeChoices.value,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'datatype',
|
||||
|
||||
@@ -29,6 +29,8 @@ import { defineComponent, computed, PropType } from '@vue/composition-api';
|
||||
import displays from '@/displays/';
|
||||
import { FancySelectItem } from '@/components/v-fancy-select/types';
|
||||
import { Field } from '@/stores/fields/types';
|
||||
import { localTypeGroups } from './index';
|
||||
import { LocalType } from './types';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@@ -40,14 +42,30 @@ export default defineComponent({
|
||||
type: Object as PropType<Field>,
|
||||
required: true,
|
||||
},
|
||||
localType: {
|
||||
type: String as PropType<LocalType>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const items = computed<FancySelectItem[]>(() => {
|
||||
return displays.map((inter) => ({
|
||||
text: inter.name,
|
||||
value: inter.id,
|
||||
icon: inter.icon,
|
||||
}));
|
||||
return (
|
||||
displays
|
||||
// Filter interfaces based on the localType that was selected
|
||||
.filter((display) => {
|
||||
return display.types.some((type) => localTypeGroups[props.localType].includes(type));
|
||||
})
|
||||
// When choosing an interface, the type is preset. We can safely assume that a
|
||||
// type has been set when you reach the display pane
|
||||
.filter((display) => {
|
||||
return display.types.includes(props.value.type);
|
||||
})
|
||||
.map((inter) => ({
|
||||
text: inter.name,
|
||||
value: inter.id,
|
||||
icon: inter.icon,
|
||||
}))
|
||||
);
|
||||
});
|
||||
|
||||
const selectedDisplay = computed(() => {
|
||||
|
||||
@@ -14,12 +14,7 @@
|
||||
:disabled="isNew === false"
|
||||
/>
|
||||
|
||||
<v-fancy-select
|
||||
:disabled="isNew === false"
|
||||
:items="items"
|
||||
:value="localType"
|
||||
@input="$emit('update:localType', $event)"
|
||||
/>
|
||||
<v-fancy-select :disabled="isNew === false" :items="items" :value="localType" @input="setLocalType" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -69,7 +64,7 @@ export default defineComponent({
|
||||
},
|
||||
]);
|
||||
|
||||
return { emitValue, items };
|
||||
return { emitValue, items, setLocalType };
|
||||
|
||||
function emitValue(key: string, value: any) {
|
||||
emit('input', {
|
||||
@@ -77,6 +72,17 @@ export default defineComponent({
|
||||
[key]: value,
|
||||
});
|
||||
}
|
||||
|
||||
function setLocalType(newType: string) {
|
||||
emit('update:localType', newType);
|
||||
|
||||
// Reset the interface when changing the localtype. If you change localType, the previously
|
||||
// selected interface most likely doesn't exist in the new selection anyways
|
||||
emit('input', {
|
||||
...props.value,
|
||||
interface: null,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<h2 class="type-title" v-if="isNew">{{ $t('interface_setup_title') }}</h2>
|
||||
|
||||
<v-fancy-select :items="items" :value="value.interface" @input="emitValue('interface', $event)" />
|
||||
<v-fancy-select :items="items" :value="value.interface" @input="setInterface" />
|
||||
|
||||
<template v-if="selectedInterface">
|
||||
<v-form
|
||||
@@ -29,6 +29,8 @@ import { defineComponent, computed, PropType } from '@vue/composition-api';
|
||||
import interfaces from '@/interfaces/';
|
||||
import { FancySelectItem } from '@/components/v-fancy-select/types';
|
||||
import { Field } from '@/stores/fields/types';
|
||||
import { LocalType } from './types';
|
||||
import { localTypeGroups } from './index';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@@ -40,21 +42,59 @@ export default defineComponent({
|
||||
type: Object as PropType<Field>,
|
||||
required: true,
|
||||
},
|
||||
localType: {
|
||||
type: String as PropType<LocalType>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const items = computed<FancySelectItem[]>(() => {
|
||||
return interfaces.map((inter) => ({
|
||||
text: inter.name,
|
||||
value: inter.id,
|
||||
icon: inter.icon,
|
||||
}));
|
||||
return (
|
||||
interfaces
|
||||
// Filter interfaces based on the localType that was selected
|
||||
.filter((inter) => {
|
||||
return inter.types.some((type) => localTypeGroups[props.localType].includes(type));
|
||||
})
|
||||
.filter((inter) => {
|
||||
if (props.value.type && props.isNew === false) {
|
||||
return inter.types.includes(props.value.type);
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
.map((inter) => ({
|
||||
text: inter.name,
|
||||
value: inter.id,
|
||||
icon: inter.icon,
|
||||
}))
|
||||
);
|
||||
});
|
||||
|
||||
const selectedInterface = computed(() => {
|
||||
return interfaces.find((inter) => inter.id === props.value.interface) || null;
|
||||
});
|
||||
|
||||
return { emitValue, items, selectedInterface };
|
||||
return { emitValue, items, selectedInterface, setInterface };
|
||||
|
||||
function setInterface(value: string | null) {
|
||||
if (value === null) {
|
||||
return emit('input', {
|
||||
...props.value,
|
||||
interface: null,
|
||||
});
|
||||
}
|
||||
|
||||
const chosenInterface = interfaces.find((inter) => inter.id === value);
|
||||
|
||||
if (!chosenInterface) return;
|
||||
|
||||
// This also presets the field type
|
||||
emit('input', {
|
||||
...props.value,
|
||||
interface: value,
|
||||
type: chosenInterface.types[0],
|
||||
});
|
||||
}
|
||||
|
||||
function emitValue(key: string, value: any) {
|
||||
emit('input', {
|
||||
|
||||
@@ -74,8 +74,9 @@ import useFieldsStore from '@/stores/fields/';
|
||||
import { Relation } from '@/stores/relations/types';
|
||||
import api from '@/api';
|
||||
import useProjectsStore from '@/stores/projects';
|
||||
|
||||
import { LocalType } from './types';
|
||||
import { localTypeGroups } from './index';
|
||||
import { Type } from '@/stores/fields/types';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@@ -109,7 +110,7 @@ export default defineComponent({
|
||||
const fieldsStore = useFieldsStore();
|
||||
const projectsStore = useProjectsStore();
|
||||
|
||||
const { field, localType } = usefield();
|
||||
const { field, localType } = useField();
|
||||
const { tabs, currentTab } = useTabs();
|
||||
const { save, saving } = useSave();
|
||||
|
||||
@@ -117,7 +118,7 @@ export default defineComponent({
|
||||
|
||||
return { field, tabs, currentTab, localType, save, saving, newRelations };
|
||||
|
||||
function usefield() {
|
||||
function useField() {
|
||||
const defaults = {
|
||||
id: null,
|
||||
collection: props.collection,
|
||||
@@ -166,16 +167,13 @@ export default defineComponent({
|
||||
if (existingField) {
|
||||
field.value = existingField;
|
||||
|
||||
const type = existingField.type.toLowerCase();
|
||||
const type: Type = existingField.type;
|
||||
|
||||
if (type === 'file') {
|
||||
localType.value = 'file';
|
||||
} else if (type === 'files') {
|
||||
localType.value = 'files';
|
||||
} else if (['o2m', 'm2o', 'm2m'].includes(type)) {
|
||||
localType.value = 'relational';
|
||||
} else {
|
||||
localType.value = 'standard';
|
||||
for (const [group, types] of Object.entries(localTypeGroups)) {
|
||||
if (types.includes(type)) {
|
||||
localType.value = group as LocalType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
field.value = { ...defaults };
|
||||
|
||||
@@ -1,4 +1,21 @@
|
||||
import FieldSetup from './field-setup.vue';
|
||||
import { types, Type } from '@/stores/fields/types';
|
||||
import { LocalType } from './types';
|
||||
|
||||
const localTypeGroups: Record<LocalType, Type[]> = {
|
||||
relational: ['m2o', 'o2m', 'm2m', 'translation'],
|
||||
file: ['file'],
|
||||
files: ['files'],
|
||||
standard: [],
|
||||
};
|
||||
|
||||
localTypeGroups.standard = types.filter((typeName: Type) => {
|
||||
return (
|
||||
[...localTypeGroups.relational, ...localTypeGroups.file, ...localTypeGroups.files].includes(typeName) === false
|
||||
);
|
||||
});
|
||||
|
||||
export { localTypeGroups };
|
||||
|
||||
export { FieldSetup };
|
||||
export default FieldSetup;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
/>
|
||||
<permissions-toggle
|
||||
type="read"
|
||||
:options="ownerField ? ['none', 'mine', 'role', 'full'] : ['none', 'full']"
|
||||
:options="userCreatedField ? ['none', 'mine', 'role', 'full'] : ['none', 'full']"
|
||||
:value="getCombinedPermission('read')"
|
||||
:save-permission="saveForAllStatuses"
|
||||
:collection="collection"
|
||||
@@ -21,7 +21,7 @@
|
||||
/>
|
||||
<permissions-toggle
|
||||
type="update"
|
||||
:options="ownerField ? ['none', 'mine', 'role', 'full'] : ['none', 'full']"
|
||||
:options="userCreatedField ? ['none', 'mine', 'role', 'full'] : ['none', 'full']"
|
||||
:value="getCombinedPermission('update')"
|
||||
:save-permission="saveForAllStatuses"
|
||||
:collection="collection"
|
||||
@@ -29,7 +29,7 @@
|
||||
/>
|
||||
<permissions-toggle
|
||||
type="delete"
|
||||
:options="ownerField ? ['none', 'mine', 'role', 'full'] : ['none', 'full']"
|
||||
:options="userCreatedField ? ['none', 'mine', 'role', 'full'] : ['none', 'full']"
|
||||
:value="getCombinedPermission('delete')"
|
||||
:save-permission="saveForAllStatuses"
|
||||
:collection="collection"
|
||||
@@ -117,7 +117,7 @@
|
||||
/>
|
||||
<permissions-toggle
|
||||
type="read"
|
||||
:options="ownerField ? ['none', 'mine', 'role', 'full'] : ['none', 'full']"
|
||||
:options="userCreatedField ? ['none', 'mine', 'role', 'full'] : ['none', 'full']"
|
||||
:value="getPermissionValue('read', status.value)"
|
||||
:status="status.value"
|
||||
:save-permission="savePermission"
|
||||
@@ -127,7 +127,7 @@
|
||||
/>
|
||||
<permissions-toggle
|
||||
type="update"
|
||||
:options="ownerField ? ['none', 'mine', 'role', 'full'] : ['none', 'full']"
|
||||
:options="userCreatedField ? ['none', 'mine', 'role', 'full'] : ['none', 'full']"
|
||||
:value="getPermissionValue('update', status.value)"
|
||||
:status="status.value"
|
||||
:save-permission="savePermission"
|
||||
@@ -137,7 +137,7 @@
|
||||
/>
|
||||
<permissions-toggle
|
||||
type="delete"
|
||||
:options="ownerField ? ['none', 'mine', 'role', 'full'] : ['none', 'full']"
|
||||
:options="userCreatedField ? ['none', 'mine', 'role', 'full'] : ['none', 'full']"
|
||||
:value="getPermissionValue('delete', status.value)"
|
||||
:status="status.value"
|
||||
:save-permission="savePermission"
|
||||
@@ -241,7 +241,7 @@ export default defineComponent({
|
||||
},
|
||||
setup(props) {
|
||||
const { collection } = toRefs(props);
|
||||
const { fields, info, statusField, ownerField } = useCollection(collection);
|
||||
const { fields, info, statusField, userCreatedField } = useCollection(collection);
|
||||
|
||||
const detailsOpen = ref(false);
|
||||
|
||||
@@ -289,7 +289,7 @@ export default defineComponent({
|
||||
statuses,
|
||||
detailsOpen,
|
||||
permissions,
|
||||
ownerField,
|
||||
userCreatedField,
|
||||
getPermissionValue,
|
||||
getCombinedPermission,
|
||||
saveForAllStatuses,
|
||||
|
||||
Reference in New Issue
Block a user