Use technical collection/field names in settings (#19444)

* temp commit

* change to non translated names

* Add changeset

* Fix linter warning

* Update related collection selection as well

Unfortunately, modifiying the font via css var doesn't work so we need
to pass it down via prop. See https://github.com/vuejs/core/issues/7312

---------

Co-authored-by: Pascal Jufer <pascal-jufer@bluewin.ch>
Co-authored-by: Rijk van Zanten <rijkvanzanten@me.com>
This commit is contained in:
Nitwel
2023-08-21 11:40:04 +02:00
committed by GitHub
parent b689da7a21
commit 1aeaebea31
18 changed files with 114 additions and 62 deletions

View File

@@ -0,0 +1,5 @@
---
"@directus/app": patch
---
Switched to technical collection/field names in settings for clarity

View File

@@ -3,11 +3,15 @@
v-if="field.children || supportedFunctions.length > 0"
:clickable="!field.disabled && (relationalFieldSelectable || !field.relatedCollection)"
:value="field.path"
:class="{ 'raw-field-names': rawFieldNames }"
@click="$emit('add', [field.key])"
>
<template #activator>
<v-list-item-content>
<v-text-overflow :text="field.name || formatTitle(field.field)" :highlight="search" />
<v-text-overflow
:text="rawFieldNames ? field.field : field.name || formatTitle(field.field)"
:highlight="search"
/>
</v-list-item-content>
</template>
@@ -24,7 +28,7 @@
</v-list-item-icon>
<v-list-item-content>
<v-text-overflow
:text="`${t(`functions.${fn}`)} (${field.name || formatTitle(field.field)})`"
:text="`${t(`functions.${fn}`)} (${rawFieldNames ? field.field : field.name || formatTitle(field.field)})`"
:highlight="search"
/>
</v-list-item-content>
@@ -50,13 +54,23 @@
:relational-field-selectable="relationalFieldSelectable"
:parent="field.field"
:allow-select-all="allowSelectAll"
:raw-field-names="rawFieldNames"
@add="$emit('add', $event)"
/>
</v-list-group>
<v-list-item v-else :disabled="field.disabled" clickable @click="$emit('add', [field.key])">
<v-list-item
v-else
:disabled="field.disabled"
:class="{ 'raw-field-names': rawFieldNames }"
clickable
@click="$emit('add', [field.key])"
>
<v-list-item-content>
<v-text-overflow :text="field.name || formatTitle(field.field)" :highlight="search" />
<v-text-overflow
:text="rawFieldNames ? field.field : field.name || formatTitle(field.field)"
:highlight="search"
/>
</v-list-item-content>
</v-list-item>
</template>
@@ -86,6 +100,7 @@ interface Props {
relationalFieldSelectable?: boolean;
allowSelectAll?: boolean;
parent?: string | null;
rawFieldNames?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
@@ -94,6 +109,7 @@ const props = withDefaults(defineProps<Props>(), {
relationalFieldSelectable: true,
allowSelectAll: false,
parent: null,
rawFieldNames: false,
});
const emit = defineEmits(['add']);
@@ -129,4 +145,8 @@ const addAll = () => {
--v-icon-color: var(--primary);
--v-list-item-color: var(--primary);
}
.raw-field-names {
--v-list-item-content-font-family: var(--family-monospace);
}
</style>

View File

@@ -27,6 +27,7 @@
:include-functions="includeFunctions"
:relational-field-selectable="relationalFieldSelectable"
:allow-select-all="allowSelectAll"
:raw-field-names="rawFieldNames"
@add="$emit('add', $event)"
/>
</v-list>
@@ -49,6 +50,7 @@ interface Props {
includeRelations?: boolean;
relationalFieldSelectable?: boolean;
allowSelectAll?: boolean;
rawFieldNames?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
@@ -58,6 +60,7 @@ const props = withDefaults(defineProps<Props>(), {
includeRelations: true,
relationalFieldSelectable: true,
allowSelectAll: false,
rawFieldNames: false,
});
const emit = defineEmits(['add']);

View File

@@ -27,6 +27,7 @@
<select-list-item-group
v-if="childItem.children"
:item="childItem"
:item-label-font-family="itemLabelFontFamily"
:model-value="modelValue"
:multiple="multiple"
:allow-other="allowOther"
@@ -37,6 +38,7 @@
v-else
:model-value="modelValue"
:item="childItem"
:item-label-font-family="itemLabelFontFamily"
:multiple="multiple"
:allow-other="allowOther"
@update:model-value="$emit('update:modelValue', $event)"
@@ -52,6 +54,7 @@ import SelectListItem from './select-list-item.vue';
interface Props {
item: Option;
itemLabelFontFamily?: string;
modelValue?: string | number | (string | number)[] | null;
multiple?: boolean;
allowOther?: boolean;

View File

@@ -17,6 +17,7 @@
<span v-if="multiple === false || item.selectable === false" class="item-text">{{ item.text }}</span>
<v-checkbox
v-else
class="checkbox"
:model-value="modelValue || []"
:label="item.text"
:value="item.value"
@@ -33,12 +34,14 @@ import { Option } from './types';
interface Props {
item: Option;
itemLabelFontFamily: string;
modelValue?: string | number | (string | number)[] | null;
multiple?: boolean;
allowOther?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
itemLabelFontFamily: 'var(--v-select-font-family)',
modelValue: null,
multiple: true,
allowOther: false,
@@ -58,3 +61,9 @@ const isActive = computed(() => {
}
});
</script>
<style scoped>
.checkbox :deep(.type-text) {
font-family: v-bind('$props.itemLabelFontFamily');
}
</style>

View File

@@ -68,6 +68,7 @@
<select-list-item-group
v-if="item.children"
:item="item"
:item-label-font-family="itemLabelFontFamily"
:model-value="modelValue"
:multiple="multiple"
:allow-other="allowOther"
@@ -78,6 +79,7 @@
v-else
:model-value="modelValue"
:item="item"
:item-label-font-family="itemLabelFontFamily"
:multiple="multiple"
:allow-other="allowOther"
@update:model-value="$emit('update:modelValue', $event)"
@@ -155,6 +157,8 @@ interface Props {
itemValue?: string;
/** Which key in items is used to show an icon */
itemIcon?: string | null;
/** Which font family to use for checkbox item label */
itemLabelFontFamily?: string;
/** Which key in items is used to model the disabled state */
itemDisabled?: string;
/** Which key in items is used to model the selectable state */
@@ -368,10 +372,6 @@ function useDisplayValue() {
--v-list-min-width: 0;
}
.item-text {
font-family: var(--v-select-font-family);
}
.v-input {
--v-input-font-family: var(--v-select-font-family);

View File

@@ -14,9 +14,11 @@
<template #item="{ element, index }">
<li class="row">
<div v-if="filterInfo[index].isField" block class="node field">
<div class="header" :class="{ inline }">
<div class="header" :class="{ inline, 'raw-field-names': rawFieldNames }">
<v-icon name="drag_indicator" class="drag-handle" small></v-icon>
<span v-if="field || !isExistingField(element)" class="plain-name">{{ getFieldPreview(element) }}</span>
<span v-if="field || !isExistingField(element)" class="plain-name">
{{ getFieldPreview(element) }}
</span>
<v-menu v-else placement="bottom-start" show-arrow>
<template #activator="{ toggle }">
<button class="name" @click="toggle">
@@ -31,6 +33,7 @@
:include-relations="includeRelations"
:relational-field-selectable="relationalFieldSelectable"
:allow-select-all="false"
:raw-field-names="rawFieldNames"
@add="updateField(index, $event[0])"
/>
</v-menu>
@@ -89,6 +92,7 @@
:collection="collection"
:depth="depth + 1"
:inline="inline"
:raw-field-names="rawFieldNames"
@change="$emit('change')"
@remove-node="$emit('remove-node', [`${index}.${filterInfo[index].name}`, ...$event])"
@update:filter="replaceNode(index, { [filterInfo[index].name]: $event })"
@@ -146,6 +150,7 @@ interface Props {
includeValidation?: boolean;
includeRelations?: boolean;
relationalFieldSelectable?: boolean;
rawFieldNames?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
@@ -155,6 +160,7 @@ const props = withDefaults(defineProps<Props>(), {
includeValidation: false,
includeRelations: true,
relationalFieldSelectable: true,
rawFieldNames: false,
});
const emit = defineEmits(['remove-node', 'update:filter', 'change']);
@@ -211,7 +217,7 @@ function getFieldPreview(node: Record<string, any>) {
const pathPrefix = fieldParts.slice(0, index);
const field = fieldsStore.getField(props.collection, [...pathPrefix, key].join('.'));
const name = field?.name ?? key;
const name = (props.rawFieldNames ? field?.field : field?.name) ?? key;
if (hasFunction) {
return t(`functions.${functionName}`) + ` (${name})`;
@@ -428,6 +434,13 @@ function isExistingField(node: Record<string, any>): boolean {
white-space: nowrap;
}
&.raw-field-names {
.plain-name,
.name {
font-family: var(--family-monospace);
}
}
.name,
.comparator {
position: relative;

View File

@@ -21,6 +21,7 @@
:include-validation="includeValidation"
:include-relations="includeRelations"
:relational-field-selectable="relationalFieldSelectable"
:raw-field-names="rawFieldNames"
@remove-node="removeNode($event)"
@change="emitValue"
/>
@@ -47,6 +48,7 @@
:include-relations="includeRelations"
:relational-field-selectable="relationalFieldSelectable"
:allow-select-all="false"
:raw-field-names="rawFieldNames"
@add="addNode($event[0])"
>
<template #prepend>
@@ -114,6 +116,7 @@ interface Props {
includeValidation?: boolean;
includeRelations?: boolean;
relationalFieldSelectable?: boolean;
rawFieldNames?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
@@ -127,6 +130,7 @@ const props = withDefaults(defineProps<Props>(), {
includeValidation: false,
includeRelations: true,
relationalFieldSelectable: true,
rawFieldNames: false,
});
const emit = defineEmits(['input']);

View File

@@ -21,7 +21,8 @@
:placeholder="t('collection') + '...'"
:items="availableCollections"
item-value="collection"
item-text="name"
item-text="collection"
item-label-font-family="var(--family-monospace)"
item-disabled="meta.singleton"
multiple
:is-menu-same-width="false"
@@ -188,21 +189,17 @@ const isExisting = computed(() => editing.value !== '+');
const currentPrimaryKey = computed(() => fieldsStore.getPrimaryKeyFieldForCollection(collection.value!)?.field);
const availableCollections = computed(() => {
return orderBy(
[
...collectionsStore.databaseCollections,
{
divider: true,
},
{
name: t('system'),
selectable: false,
children: collectionsStore.crudSafeSystemCollections,
},
],
['collection'],
['asc']
);
return [
...orderBy(collectionsStore.databaseCollections, ['collection'], ['asc']),
{
divider: true,
},
{
collection: t('system'),
selectable: false,
children: orderBy(collectionsStore.crudSafeSystemCollections, ['collection'], ['asc']),
},
];
});
const unsortableJunctionFields = computed(() => {

View File

@@ -58,7 +58,8 @@
:placeholder="t('collection') + '...'"
:items="availableCollections"
item-value="collection"
item-text="name"
item-text="collection"
item-label-font-family="var(--family-monospace)"
item-disabled="meta.singleton"
multiple
:multiple-preview-threshold="0"
@@ -90,21 +91,17 @@ const o2mField = syncFieldDetailStoreProperty('relations.o2m.field');
const oneAllowedCollections = syncFieldDetailStoreProperty('relations.m2o.meta.one_allowed_collections', []);
const availableCollections = computed(() => {
return orderBy(
[
...collectionsStore.databaseCollections,
{
divider: true,
},
{
name: t('system'),
selectable: false,
children: collectionsStore.crudSafeSystemCollections,
},
],
['collection'],
['asc']
);
return [
...orderBy(collectionsStore.databaseCollections, ['collection'], ['asc']),
{
divider: true,
},
{
collection: t('system'),
selectable: false,
children: orderBy(collectionsStore.crudSafeSystemCollections, ['collection'], ['asc']),
},
];
});
</script>

View File

@@ -97,12 +97,12 @@ const simple = ref(props.type === null);
const title = computed(() => {
const existingField = fieldsStore.getField(props.collection, props.field);
const fieldName = existingField?.name || formatTitle(fieldDetail.field.name || '');
const fieldName = formatTitle(existingField?.field || fieldDetail.field.field || '');
if (props.field === '+' && fieldName === '') {
return t('creating_new_field', { collection: collectionInfo.value?.name });
return t('creating_new_field', { collection: collectionInfo.value?.collection });
} else {
return t('field_in_collection', { field: fieldName, collection: collectionInfo.value?.name });
return t('field_in_collection', { field: fieldName, collection: collectionInfo.value?.collection });
}
});

View File

@@ -1,5 +1,5 @@
<template>
<private-view :title="collectionInfo && collectionInfo.name">
<private-view :title="collectionInfo && formatTitle(collectionInfo.collection)">
<template #headline>
<v-breadcrumb :items="[{ name: t('settings_data_model'), to: '/settings/data-model' }]" />
</template>
@@ -110,6 +110,7 @@ import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
import SettingsNavigation from '../../../components/navigation.vue';
import FieldsManagement from './components/fields-management.vue';
import formatTitle from '@directus/format-title';
const props = defineProps<{
collection: string;

View File

@@ -453,7 +453,7 @@ function useForm() {
options: {
choices: collectionsStore.collections
.map((collection) => ({
text: collection.name,
text: collection.collection,
value: collection.collection,
}))
.filter((option) => {
@@ -543,6 +543,7 @@ function useForm() {
width: 'half',
options: {
collectionField: 'collection',
rawFieldNames: true,
},
},
},

View File

@@ -1,7 +1,7 @@
<template>
<div class="permissions-overview-row">
<span class="name">
<span v-tooltip.left="collection.collection">{{ collection.name }}</span>
<span v-tooltip.left="collection.name">{{ collection.collection }}</span>
<span class="actions">
<span class="all" @click="setFullAccessAll">{{ t('all') }}</span>
<span class="divider">/</span>
@@ -53,11 +53,12 @@
</template>
<script setup lang="ts">
import { Collection, Permission } from '@directus/types';
import { Permission } from '@directus/types';
import { toRefs } from 'vue';
import { useI18n } from 'vue-i18n';
import useUpdatePermissions from '../composables/use-update-permissions';
import PermissionsOverviewToggle from './permissions-overview-toggle.vue';
import { Collection } from '@/types/collections';
const props = defineProps<{
collection: Collection;
@@ -89,6 +90,7 @@ function isLoading(action: string) {
.name {
flex-grow: 1;
font-family: var(--family-monospace);
.actions {
margin-left: 8px;

View File

@@ -51,7 +51,7 @@ const fieldsInCollection = computed(() => {
return fields.map((field: Field) => {
return {
text: field.name,
text: field.field,
value: field.field,
};
});
@@ -92,6 +92,10 @@ const fields = computed({
margin-bottom: 36px;
}
.checkboxes :deep(.v-checkbox .type-text) {
font-family: var(--family-monospace);
}
.app-minimal {
.v-divider {
margin: 24px 0;

View File

@@ -46,6 +46,7 @@ const fields = computed(() => [
interface: 'system-filter',
options: {
collectionName: permissionSync.value.collection,
rawFieldNames: true,
},
},
},

View File

@@ -41,6 +41,7 @@ const fields = computed(() => [
collectionName: permissionSync.value.collection,
includeValidation: true,
includeRelations: false,
rawFieldNames: true,
},
},
},

View File

@@ -47,7 +47,6 @@
<script setup lang="ts">
import api from '@/api';
import { useDialogRoute } from '@/composables/use-dialog-route';
import { useCollectionsStore } from '@/stores/collections';
import { isPermissionEmpty } from '@/utils/is-permission-empty';
import { unexpectedError } from '@/utils/unexpected-error';
import { Permission, Role } from '@directus/types';
@@ -73,28 +72,20 @@ const { t } = useI18n();
const router = useRouter();
const collectionsStore = useCollectionsStore();
const isOpen = useDialogRoute();
const permission = ref<Permission>();
const role = ref<Role>();
const loading = ref(false);
const collectionName = computed(() => {
if (!permission.value) return null;
return collectionsStore.collections.find((collection) => collection.collection === permission.value!.collection)
?.name;
});
const modalTitle = computed(() => {
if (loading.value || !permission.value) return t('loading');
if (props.roleKey) {
return role.value!.name + ' -> ' + collectionName.value + ' -> ' + t(permission.value.action);
return role.value!.name + ' -> ' + permission.value!.collection + ' -> ' + t(permission.value.action);
}
return t('public_label') + ' -> ' + collectionName.value + ' -> ' + t(permission.value.action);
return t('public_label') + ' -> ' + permission.value!.collection + ' -> ' + t(permission.value.action);
});
watch(() => props.permissionKey, load, { immediate: true });