Merge pull request #287 from directus/suggest-m2o

suggest fields on m2o
This commit is contained in:
Rijk van Zanten
2020-09-24 16:29:02 -04:00
committed by GitHub
8 changed files with 109 additions and 19 deletions

View File

@@ -27,7 +27,7 @@ export default async function runAST(originalAST: AST, options?: RunASTOptions):
const rawItems: Item | Item[] = await dbQuery;
if (!rawItems || (Array.isArray(rawItems) && rawItems.length === 0)) return null;
if (!rawItems) return null;
// Run the items through the special transforms
const payloadService = new PayloadService(ast.name, { knex });

View File

@@ -13,6 +13,7 @@
:key="childField.field"
:parent="`${parent ? parent + '.' : ''}${field.field}`"
:field="childField"
:depth="depth - 1"
@add="$emit('add', $event)"
/>
</v-list-group>
@@ -33,6 +34,10 @@ export default defineComponent({
type: String,
default: null,
},
depth: {
type: Number,
default: 2,
},
},
});
</script>

View File

@@ -22,7 +22,7 @@
</template>
<v-list dense>
<field-list-item @add="addField" v-for="field in tree" :key="field.field" :field="field" />
<field-list-item @add="addField" v-for="field in tree" :key="field.field" :field="field" :depth="depth" />
</v-list>
</v-menu>
</template>
@@ -33,6 +33,7 @@ import FieldListItem from './field-list-item.vue';
import { useFieldsStore } from '@/stores';
import { Field } from '@/types/';
import useFieldTree from '@/composables/use-field-tree';
import { FieldTree } from './types';
export default defineComponent({
components: { FieldListItem },
@@ -49,6 +50,10 @@ export default defineComponent({
type: String,
required: true,
},
depth: {
type: Number,
default: 2,
},
},
setup(props, { emit }) {
const fieldsStore = useFieldsStore();
@@ -149,7 +154,9 @@ export default defineComponent({
function addField(fieldKey: string) {
if (!contentEl.value) return;
const field: Field | null = fieldsStore.getField(props.collection, fieldKey);
const field = findTree(tree.value, fieldKey.split('.'));
if (!field) return;
const button = document.createElement('button');
@@ -157,6 +164,12 @@ export default defineComponent({
button.setAttribute('contenteditable', 'false');
button.innerText = String(field.name);
if (window.getSelection()?.rangeCount == 0) {
const range = document.createRange();
range.selectNodeContents(contentEl.value.children[0]);
window.getSelection()?.addRange(range);
}
const range = window.getSelection()?.getRangeAt(0);
if (!range) return;
range.deleteContents();
@@ -176,6 +189,16 @@ export default defineComponent({
onInput();
}
function findTree(tree: FieldTree[] | undefined, fieldSections: string[]): FieldTree | undefined {
if (tree === undefined) return undefined;
const fieldObject = tree.find((f) => f.field === fieldSections[0]);
if (fieldObject === undefined) return undefined;
if (fieldSections.length === 1) return fieldObject;
return findTree(fieldObject.children, fieldSections.slice(1));
}
function joinElements(first: HTMLElement, second: HTMLElement) {
first.innerText += second.innerText;
second.remove();
@@ -241,7 +264,7 @@ export default defineComponent({
return `<span class="text">${part}</span>`;
}
const fieldKey = part.replaceAll(/({|})/g, '').trim();
const field: Field | null = fieldsStore.getField(props.collection, fieldKey);
const field = findTree(tree.value, fieldKey.split('.'));
if (!field) return '';

View File

@@ -1,4 +1,4 @@
import useFieldTree from './use-field-tree';
import useFieldTree, { filterTree, findTree } from './use-field-tree';
export default useFieldTree;
export { useFieldTree };
export { useFieldTree, filterTree, findTree };

View File

@@ -13,7 +13,7 @@ export default function useFieldTree(collection: Ref<string>, showHidden = false
.filter((field: Field) => {
let shown = (field.meta?.special || []).includes('alias') === false;
if (showHidden === false && field.meta?.hidden === false) {
if (showHidden === false && field.meta?.hidden === true) {
shown = false;
}

View File

@@ -1,5 +1,6 @@
import { defineInterface } from '../define';
import InterfaceManyToOne from './many-to-one.vue';
import Options from './options.vue';
export default defineInterface(({ i18n }) => ({
id: 'many-to-one',
@@ -9,16 +10,6 @@ export default defineInterface(({ i18n }) => ({
component: InterfaceManyToOne,
types: ['uuid', 'string', 'text', 'integer', 'bigInteger'],
relationship: 'm2o',
options: [
{
field: 'template',
name: i18n.t('interfaces.many-to-one.display_template'),
type: 'string',
meta: {
width: 'half',
interface: 'text-input',
},
},
],
options: Options,
recommendedDisplays: ['related-values'],
}));

View File

@@ -0,0 +1,71 @@
<template>
<v-notice class="full" 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('interfaces.many-to-one.display_template') }}</p>
<v-field-template :collection="relatedCollection" v-model="template" :depth="2"></v-field-template>
</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: {
collection: {
type: String,
required: true,
},
fieldData: {
type: Object as PropType<Field>,
default: null,
},
relations: {
type: Array as PropType<Relation[]>,
default: () => [],
},
value: {
type: Object as PropType<any>,
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 relation = props.relations.find(
(relation) => relation.many_collection === props.collection && relation.many_field === field
);
return relation?.one_collection || null;
});
return { template, relatedCollection };
},
});
</script>
<style lang="scss" scoped>
@import '@/styles/mixins/form-grid';
.form-grid {
@include form-grid;
}
</style>

View File

@@ -52,7 +52,7 @@ export const useRelationsStore = createStore({
] as Relation[];
}
const relations = this.getRelationsForCollection(collection).filter((relation: Relation) => {
const relations: Relation[] = this.getRelationsForCollection(collection).filter((relation: Relation) => {
return relation.many_field === field || relation.one_field === field;
});