From 21bb26988eaf31406e97aa707904f583fd9c96fa Mon Sep 17 00:00:00 2001 From: Nitwel Date: Fri, 24 Feb 2023 15:01:57 +0100 Subject: [PATCH] Improve preview of relational columns in tabular layout (#15269) * improve preview of relational tabular columns * update pnpm-lock * Use the improved get method (#15548) * Update app/src/layouts/tabular/index.ts --------- Co-authored-by: Azri Kahar <42867097+azrikahar@users.noreply.github.com> Co-authored-by: Brainslug Co-authored-by: Rijk van Zanten Co-authored-by: Pascal Jufer --- app/src/layouts/tabular/index.ts | 39 +++++++++++++++++++ app/src/stores/relations.ts | 39 +++++++++++++++++++ .../shared/src/utils/get-with-arrays.test.ts | 5 +++ packages/shared/src/utils/get-with-arrays.ts | 7 +++- 4 files changed, 88 insertions(+), 2 deletions(-) diff --git a/app/src/layouts/tabular/index.ts b/app/src/layouts/tabular/index.ts index 82fba192bd..e1ecdeee13 100644 --- a/app/src/layouts/tabular/index.ts +++ b/app/src/layouts/tabular/index.ts @@ -17,6 +17,7 @@ import TabularActions from './actions.vue'; import TabularOptions from './options.vue'; import TabularLayout from './tabular.vue'; import { LayoutOptions, LayoutQuery } from './types'; +import { useRelationsStore } from '@/stores/relations'; export default defineLayout({ id: 'tabular', @@ -32,6 +33,7 @@ export default defineLayout({ const router = useRouter(); const fieldsStore = useFieldsStore(); + const relationsStore = useRelationsStore(); const selection = useSync(props, 'selection', emit); const layoutOptions = useSync(props, 'layoutOptions', emit); @@ -226,6 +228,43 @@ export default defineLayout({ }); description = fieldNames.join(' -> '); + + const types = relationsStore.getRelationTypes(collection.value!, field.key); + + if (types.at(-1) === 'o2m') { + const arrayField = fieldsStore.getField(collection.value!, fieldParts.slice(0, -1).join('.')); + let display; + let displayOptions; + + if (arrayField?.meta?.display) { + display = arrayField.meta.display; + displayOptions = arrayField.meta.display_options; + } else { + display = 'related-values'; + displayOptions = { + template: `{{${fieldParts.at(-1)}}}`, + }; + } + + if (arrayField) + return { + text: field.name, + value: arrayField.field, + description, + width: localWidths.value[field.key] || layoutOptions.value?.widths?.[field.key] || null, + align: layoutOptions.value?.align?.[field.key] || 'left', + field: { + display, + displayOptions, + interface: arrayField.meta?.interface, + interfaceOptions: arrayField.meta?.options, + type: arrayField.type, + field: arrayField.field, + collection: arrayField.collection, + }, + sortable: ['json', 'alias', 'presentation', 'translations'].includes(arrayField.type) === false, + } as HeaderRaw; + } } return { diff --git a/app/src/stores/relations.ts b/app/src/stores/relations.ts index 1d6cef2741..617cf20c99 100644 --- a/app/src/stores/relations.ts +++ b/app/src/stores/relations.ts @@ -2,6 +2,7 @@ import api from '@/api'; import { useFieldsStore } from '@/stores/fields'; import { unexpectedError } from '@/utils/unexpected-error'; import { Relation, DeepPartial } from '@directus/shared/types'; +import { getRelationType } from '@directus/shared/utils'; import { isEqual } from 'lodash'; import { defineStore } from 'pinia'; @@ -104,5 +105,43 @@ export const useRelationsStore = defineStore({ return relations.find((relation) => relation.collection === collection && relation.field === field) || null; }, + /** + * Get a list of all relation types the path is made of + * @param collection The starting collection + * @param path The path to digest + * @returns An array of types of the relations + */ + getRelationTypes(collection: string, path: string): ('m2a' | 'o2m' | 'm2o')[] { + if (!path.includes('.')) return []; + + const parts = path.split('.'); + + const currentField = parts[0].includes(':') ? parts[0].split(':')[0] : parts[0]; + + const relation = this.getRelationsForField(collection, currentField).find( + (relation) => relation.collection === collection || relation.related_collection === collection + ); + + if (!relation) return []; + + const type = getRelationType({ + collection: collection, + field: currentField, + relation, + }); + + switch (type) { + case 'o2m': + return ['o2m', ...this.getRelationTypes(relation.collection, parts.slice(1).join('.'))]; + case 'm2o': + if (!relation.related_collection) return []; + return ['m2o', ...this.getRelationTypes(relation.related_collection, parts.slice(1).join('.'))]; + case 'm2a': + if (!relation.meta?.one_allowed_collections) return ['m2a']; + return ['m2a', ...this.getRelationTypes(parts[0].split(':')[1], parts.slice(1).join('.'))]; + } + + return []; + }, }, }); diff --git a/packages/shared/src/utils/get-with-arrays.test.ts b/packages/shared/src/utils/get-with-arrays.test.ts index c5815f365a..a06b3d1e50 100644 --- a/packages/shared/src/utils/get-with-arrays.test.ts +++ b/packages/shared/src/utils/get-with-arrays.test.ts @@ -16,3 +16,8 @@ test('Returns values in array path as flattened array', () => { const input = { test: [{ path: 'example' }, { path: 'another' }] }; expect(get(input, 'test.path')).toEqual(['example', 'another']); }); + +test('Returns values in array path as flattened array', () => { + const input = { test: [{ path: 'example' }, { falsePath: 'another' }] }; + expect(get(input, 'test:collection.path')).toEqual(['example']); +}); diff --git a/packages/shared/src/utils/get-with-arrays.ts b/packages/shared/src/utils/get-with-arrays.ts index 5c2af613ed..d4c081f149 100644 --- a/packages/shared/src/utils/get-with-arrays.ts +++ b/packages/shared/src/utils/get-with-arrays.ts @@ -9,9 +9,12 @@ * ``` */ export function get(object: Record | any[], path: string, defaultValue?: unknown): any { - const [key, ...follow] = path.split('.'); + let key = path.split('.')[0]!; + const follow = path.split('.').slice(1); - const result = Array.isArray(object) ? object.map((entry) => entry?.[key!]) : object?.[key!]; + if (key.includes(':')) key = key.split(':')[0]!; + + const result = Array.isArray(object) ? object.map((entry) => entry?.[key]).filter((entry) => entry) : object?.[key]; if (follow.length > 0) { return get(result, follow.join('.'), defaultValue);