mirror of
https://github.com/directus/directus.git
synced 2026-04-25 03:00:53 -04:00
Show translations as group on field management
This commit is contained in:
@@ -164,7 +164,9 @@ body {
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
--v-list-item-color: var(--foreground-subdued);
|
||||
--v-list-item-color: var(--foreground-subdued) !important;
|
||||
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
@at-root {
|
||||
|
||||
@@ -200,6 +200,8 @@
|
||||
"click_here": "Click here",
|
||||
"to_manually_setup_translations": "to manually setup translations.",
|
||||
|
||||
"click_to_manage_translated_fields": "There are no translated fields yet. Click here to create them. | There is one translated field. Click here to manage it. | There are {count} translated fields. Click here to manage them.",
|
||||
|
||||
"configure_m2o": "Configure your Many-to-One Relationship...",
|
||||
"configure_o2m": "Configure your One-to-Many Relationship...",
|
||||
"configure_m2m": "Configure your Many-to-Many Relationship...",
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
<template>
|
||||
<v-dialog persistent :active="true" v-if="localType === 'translations' && translationsManual === false">
|
||||
<v-dialog
|
||||
persistent
|
||||
:active="true"
|
||||
v-if="localType === 'translations' && translationsManual === false && field === '+'"
|
||||
>
|
||||
<v-card class="auto-translations">
|
||||
<v-card-title>{{ $t('create_translations') }}</v-card-title>
|
||||
<v-card-text>
|
||||
@@ -105,6 +109,7 @@ import { Field } from '@/types';
|
||||
import router from '@/router';
|
||||
import useCollection from '@/composables/use-collection';
|
||||
import notify from '@/utils/notify';
|
||||
import { getLocalTypeForField } from '../get-local-type';
|
||||
|
||||
import { initLocalStore, state, clearLocalStore } from './store';
|
||||
|
||||
@@ -351,58 +356,16 @@ export default defineComponent({
|
||||
router.push(`/settings/data-model/${props.collection}`);
|
||||
clearLocalStore();
|
||||
}
|
||||
|
||||
function getLocalTypeForField(
|
||||
collection: string,
|
||||
field: string
|
||||
): 'standard' | 'file' | 'files' | 'o2m' | 'm2m' | 'm2o' | 'presentation' | 'translations' {
|
||||
const fieldInfo = fieldsStore.getField(collection, field);
|
||||
const relations = relationsStore.getRelationsForField(collection, field);
|
||||
|
||||
if (relations.length === 0) {
|
||||
if (fieldInfo.type === 'alias') return 'presentation';
|
||||
return 'standard';
|
||||
}
|
||||
|
||||
if (relations.length === 1) {
|
||||
const relation = relations[0];
|
||||
if (relation.one_collection === 'directus_files') return 'file';
|
||||
if (relation.many_collection === collection) return 'm2o';
|
||||
return 'o2m';
|
||||
}
|
||||
|
||||
if (relations.length === 2) {
|
||||
if ((fieldInfo.meta?.special || []).includes('translations')) {
|
||||
return 'translations';
|
||||
}
|
||||
|
||||
const relationForCurrent = relations.find(
|
||||
(relation: Relation) =>
|
||||
(relation.many_collection === collection && relation.many_field === field) ||
|
||||
(relation.one_collection === collection && relation.one_field === field)
|
||||
);
|
||||
|
||||
if (relationForCurrent?.many_collection === collection && relationForCurrent?.many_field === field)
|
||||
return 'm2o';
|
||||
|
||||
if (
|
||||
relations[0].one_collection === 'directus_files' ||
|
||||
relations[1].one_collection === 'directus_files'
|
||||
) {
|
||||
return 'files';
|
||||
} else {
|
||||
return 'm2m';
|
||||
}
|
||||
}
|
||||
|
||||
return 'standard';
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.auto-translations {
|
||||
.v-input {
|
||||
--v-input-font-family: var(--family-monospace);
|
||||
}
|
||||
|
||||
.v-notice {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
@@ -508,6 +508,10 @@ function initLocalStore(
|
||||
one_primary: type === 'files' ? 'id' : '',
|
||||
},
|
||||
];
|
||||
|
||||
if (type === 'translations') {
|
||||
state.fieldData.field = 'translations';
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
|
||||
@@ -1,6 +1,46 @@
|
||||
<template>
|
||||
<div :class="(field.meta && field.meta.width) || 'full'">
|
||||
<v-menu attached>
|
||||
<div v-if="localType === 'translations'" class="group">
|
||||
<div class="header">
|
||||
<v-icon class="drag-handle" name="drag_indicator" />
|
||||
<v-menu show-arrow>
|
||||
<template #activator="{ toggle }">
|
||||
<span class="group-options" @click="toggle">
|
||||
<span class="name" v-tooltip="field.field">{{ field.name }}</span>
|
||||
<v-icon name="expand_more" />
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<v-list dense>
|
||||
<v-list-item :to="`/settings/data-model/${field.collection}/${field.field}`">
|
||||
<v-list-item-icon><v-icon name="edit" outline /></v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
{{ $t('edit_field') }}
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
|
||||
<v-divider />
|
||||
|
||||
<v-list-item @click="deleteActive = true" class="danger">
|
||||
<v-list-item-icon><v-icon name="delete" outline /></v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
{{ $t('delete_field') }}
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</div>
|
||||
|
||||
<router-link :to="`/settings/data-model/${translationsCollection}`">
|
||||
<v-notice type="info" icon="translate">
|
||||
<div>{{ $tc('click_to_manage_translated_fields', translationsFieldsCount) }}</div>
|
||||
<div class="spacer" />
|
||||
<v-icon name="launch" />
|
||||
</v-notice>
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
<v-menu v-else attached>
|
||||
<template #activator="{ toggle, active }">
|
||||
<v-input class="field" :class="{ hidden, active }" readonly @click="openFieldDetail">
|
||||
<template #prepend>
|
||||
@@ -140,12 +180,13 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType, ref, computed } from '@vue/composition-api';
|
||||
import { Field } from '@/types';
|
||||
import { useCollectionsStore, useFieldsStore } from '@/stores/';
|
||||
import { Field, Relation } from '@/types';
|
||||
import { useCollectionsStore, useFieldsStore, useRelationsStore } from '@/stores/';
|
||||
import { getInterfaces } from '@/interfaces';
|
||||
import router from '@/router';
|
||||
import notify from '@/utils/notify';
|
||||
import { i18n } from '@/lang';
|
||||
import { getLocalTypeForField } from '../../get-local-type';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@@ -155,11 +196,12 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const relationsStore = useRelationsStore();
|
||||
const collectionsStore = useCollectionsStore();
|
||||
const fieldsStore = useFieldsStore();
|
||||
const interfaces = getInterfaces();
|
||||
|
||||
const editActive = ref(false);
|
||||
const fieldsStore = useFieldsStore();
|
||||
const collectionsStore = useCollectionsStore();
|
||||
|
||||
const { deleteActive, deleting, deleteField } = useDeleteField();
|
||||
const { duplicateActive, duplicateName, collections, duplicateTo, saveDuplicate, duplicating } = useDuplicate();
|
||||
@@ -170,6 +212,10 @@ export default defineComponent({
|
||||
|
||||
const hidden = computed(() => props.field.meta?.hidden === true);
|
||||
|
||||
const localType = computed(() => getLocalTypeForField(props.field.collection, props.field.field));
|
||||
|
||||
const { translationsCollection, translationsFieldsCount } = useTranslations();
|
||||
|
||||
return {
|
||||
interfaceName,
|
||||
editActive,
|
||||
@@ -186,6 +232,9 @@ export default defineComponent({
|
||||
openFieldDetail,
|
||||
hidden,
|
||||
toggleVisibility,
|
||||
localType,
|
||||
translationsCollection,
|
||||
translationsFieldsCount,
|
||||
};
|
||||
|
||||
function setWidth(width: string) {
|
||||
@@ -275,6 +324,30 @@ export default defineComponent({
|
||||
|
||||
router.push(`/settings/data-model/${props.field.collection}/${props.field.field}`);
|
||||
}
|
||||
|
||||
function useTranslations() {
|
||||
const translationsCollection = computed(() => {
|
||||
if (localType.value !== 'translations') return null;
|
||||
|
||||
const relation = relationsStore.state.relations.find((relation: Relation) => {
|
||||
return (
|
||||
relation.one_collection === props.field.collection && relation.one_field === props.field.field
|
||||
);
|
||||
});
|
||||
|
||||
if (!relation) return null;
|
||||
|
||||
return relation.many_collection;
|
||||
});
|
||||
|
||||
const translationsFieldsCount = computed(() => {
|
||||
const fields = fieldsStore.getFieldsForCollection(translationsCollection.value);
|
||||
|
||||
return fields.filter((field: Field) => field.meta?.hidden !== true).length;
|
||||
});
|
||||
|
||||
return { translationsCollection, translationsFieldsCount };
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -282,11 +355,6 @@ export default defineComponent({
|
||||
<style lang="scss" scoped>
|
||||
@import '@/styles/mixins/breakpoint';
|
||||
|
||||
// The default display: contents doens't play nicely with drag and drop
|
||||
.v-menu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.full,
|
||||
.fill {
|
||||
grid-column: 1 / span 2;
|
||||
@@ -373,4 +441,36 @@ export default defineComponent({
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.spacer {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.group {
|
||||
position: relative;
|
||||
top: -8px;
|
||||
left: -8px;
|
||||
width: calc(100% + 16px);
|
||||
height: calc(100% + 16px);
|
||||
margin: 8px 0;
|
||||
padding: 8px;
|
||||
background-color: var(--background-subdued);
|
||||
border-radius: var(--border-radius);
|
||||
|
||||
.header {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.drag-handle {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.group-options {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.v-notice {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -137,8 +137,8 @@ export default defineComponent({
|
||||
{
|
||||
type: 'translations',
|
||||
icon: 'translate',
|
||||
text: i18n.t('translations')
|
||||
}
|
||||
text: i18n.t('translations'),
|
||||
},
|
||||
]);
|
||||
|
||||
return {
|
||||
@@ -169,9 +169,6 @@ export default defineComponent({
|
||||
|
||||
.fields-management {
|
||||
margin-bottom: 24px;
|
||||
padding: 12px;
|
||||
background-color: var(--background-subdued);
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.field-grid {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<private-view :title="collectionInfo && collectionInfo.name">
|
||||
<template #headline>{{ $t('settings_data_model') }}</template>
|
||||
<template #title-outer:prepend>
|
||||
<v-button class="header-icon" rounded icon exact to="/settings/data-model">
|
||||
<v-button class="header-icon" rounded icon exact @click="$router.go(-1)">
|
||||
<v-icon name="arrow_back" />
|
||||
</v-button>
|
||||
</template>
|
||||
|
||||
48
app/src/modules/settings/routes/data-model/get-local-type.ts
Normal file
48
app/src/modules/settings/routes/data-model/get-local-type.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { useFieldsStore, useRelationsStore } from '@/stores';
|
||||
import { Relation } from '@/types';
|
||||
|
||||
export function getLocalTypeForField(
|
||||
collection: string,
|
||||
field: string
|
||||
): 'standard' | 'file' | 'files' | 'o2m' | 'm2m' | 'm2o' | 'presentation' | 'translations' {
|
||||
const fieldsStore = useFieldsStore();
|
||||
const relationsStore = useRelationsStore();
|
||||
|
||||
const fieldInfo = fieldsStore.getField(collection, field);
|
||||
const relations = relationsStore.getRelationsForField(collection, field);
|
||||
|
||||
if (relations.length === 0) {
|
||||
if (fieldInfo.type === 'alias') return 'presentation';
|
||||
return 'standard';
|
||||
}
|
||||
|
||||
if (relations.length === 1) {
|
||||
const relation = relations[0];
|
||||
if (relation.one_collection === 'directus_files') return 'file';
|
||||
if (relation.many_collection === collection) return 'm2o';
|
||||
return 'o2m';
|
||||
}
|
||||
|
||||
if (relations.length === 2) {
|
||||
if ((fieldInfo.meta?.special || []).includes('translations')) {
|
||||
return 'translations';
|
||||
}
|
||||
|
||||
const relationForCurrent = relations.find(
|
||||
(relation: Relation) =>
|
||||
(relation.many_collection === collection && relation.many_field === field) ||
|
||||
(relation.one_collection === collection && relation.one_field === field)
|
||||
);
|
||||
|
||||
if (relationForCurrent?.many_collection === collection && relationForCurrent?.many_field === field)
|
||||
return 'm2o';
|
||||
|
||||
if (relations[0].one_collection === 'directus_files' || relations[1].one_collection === 'directus_files') {
|
||||
return 'files';
|
||||
} else {
|
||||
return 'm2m';
|
||||
}
|
||||
}
|
||||
|
||||
return 'standard';
|
||||
}
|
||||
Reference in New Issue
Block a user