Image display (#505)

* Add adjust for displays util

* Adjust fetched fields for configured options

* Add padding to table rows

* Add image display

* Rename Query to Options

* Fix codesmell
This commit is contained in:
Rijk van Zanten
2020-04-29 18:44:13 -04:00
committed by GitHub
parent 0bf32c230b
commit 0253cc9d93
11 changed files with 138 additions and 22 deletions

View File

@@ -5,7 +5,7 @@
@click="$emit('click', $event)"
:style="{
'--table-row-height': height + 2 + 'px',
'--table-row-line-height': height + 'px',
'--table-row-line-height': 1,
}"
>
<td v-if="showManualSort" class="manual cell">
@@ -102,9 +102,13 @@ export default defineComponent({
<style lang="scss" scoped>
.table-row {
height: var(--table-row-height);
.cell {
height: var(--table-row-height);
padding: 0 0 0 12px;
display: flex;
align-items: center;
padding: 8px 0;
padding-left: 12px;
overflow: hidden;
line-height: var(--table-row-line-height);
white-space: nowrap;

View File

@@ -9,7 +9,7 @@ import filtersToQuery from '@/utils/filters-to-query';
import { orderBy } from 'lodash';
import moveInArray from '@/utils/move-in-array';
type Options = {
type Query = {
limit: Ref<number>;
fields: Ref<readonly string[]>;
sort: Ref<string>;
@@ -18,11 +18,11 @@ type Options = {
searchQuery: Ref<string | null>;
};
export function useItems(collection: Ref<string>, options: Options) {
export function useItems(collection: Ref<string>, query: Query) {
const projectsStore = useProjectsStore();
const { primaryKeyField, sortField } = useCollection(collection);
const { limit, fields, sort, page, filters, searchQuery } = options;
const { limit, fields, sort, page, filters, searchQuery } = query;
const endpoint = computed(() => {
const { currentProjectKey } = projectsStore.state;
@@ -52,7 +52,7 @@ export function useItems(collection: Ref<string>, options: Options) {
return;
}
// Waiting for the tick here makes sure the options have been adjusted for the new
// Waiting for the tick here makes sure the query have been adjusted for the new
// collection
await Vue.nextTick();
reset();

View File

@@ -0,0 +1,48 @@
<template>
<img v-if="src" :src="src" role="presentation" :alt="value && value.title" />
<span v-else>--</span>
</template>
<script lang="ts">
import { defineComponent, PropType, computed } from '@vue/composition-api';
type Image = {
type: string;
data: {
thumbnails: {
key: string;
url: string;
}[];
};
};
export default defineComponent({
props: {
value: {
type: Object as PropType<Image>,
default: null,
},
},
setup(props) {
const src = computed(() => {
if (props.value === null) return null;
return (
props.value?.data?.thumbnails?.find((thumb) => thumb.key === 'directus-small-crop')
?.url || null
);
});
return { src };
},
});
</script>
<style lang="scss" scoped>
img {
display: inline-block;
width: auto;
height: 100%;
vertical-align: -30%;
border-radius: 4px;
}
</style>

View File

@@ -0,0 +1,12 @@
import { defineDisplay } from '@/displays/define';
import DisplayImage from './image.vue';
export default defineDisplay(({ i18n }) => ({
id: 'image',
name: i18n.t('image'),
types: ['file'],
icon: 'insert_photo',
handler: DisplayImage,
options: null,
fields: ['data', 'type', 'title'],
}));

View File

@@ -4,6 +4,7 @@ import DisplayStatusDot from './status-dot/';
import DisplayStatusBadge from './status-badge/';
import DisplayTags from './tags/';
import DisplayFormattedText from './formatted-text';
import DisplayImage from './image';
export const displays = [
DisplayIcon,
@@ -12,5 +13,6 @@ export const displays = [
DisplayStatusBadge,
DisplayTags,
DisplayFormattedText,
DisplayImage,
];
export default displays;

View File

@@ -13,6 +13,7 @@ export type DisplayConfig = {
handler: DisplayHandlerFunction | Component;
options: null | Partial<Field>[] | Component;
types: string[];
fields?: string[];
};
export type DisplayContext = { i18n: VueI18n };

View File

@@ -147,6 +147,7 @@ import { render } from 'micromustache';
import useProjectsStore from '@/stores/projects';
import CardsHeader from './components/header.vue';
import i18n from '@/lang';
import adjustFieldsForDisplays from '@/utils/adjust-fields-for-displays';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type Item = Record<string, any>;
@@ -345,21 +346,26 @@ export default defineComponent({
fields.push(`${imageSource.value}.data`);
}
if (title.value) {
fields.push(...getFieldsFromTemplate(title.value));
}
if (subtitle.value) {
fields.push(...getFieldsFromTemplate(subtitle.value));
}
const sortField = sort.value.startsWith('-') ? sort.value.substring(1) : sort.value;
if (fields.includes(sortField) === false) {
fields.push(sortField);
}
return fields;
const titleSubtitleFields: string[] = [];
if (title.value) {
titleSubtitleFields.push(...getFieldsFromTemplate(title.value));
}
if (subtitle.value) {
titleSubtitleFields.push(...getFieldsFromTemplate(subtitle.value));
}
return [
...fields,
...adjustFieldsForDisplays(titleSubtitleFields, props.collection),
];
});
return { sort, limit, page, fields };

View File

@@ -270,6 +270,7 @@ export default defineComponent({
.title,
.subtitle {
width: 100%;
height: 20px;
overflow: hidden;
line-height: 1.3em;
white-space: nowrap;

View File

@@ -153,6 +153,7 @@ import useItems from '@/compositions/use-items';
import { render } from 'micromustache';
import { Filter } from '@/stores/collection-presets/types';
import i18n from '@/lang';
import adjustFieldsForDisplays from '@/utils/adjust-fields-for-displays';
type ViewOptions = {
widths?: {
@@ -224,7 +225,7 @@ export default defineComponent({
fieldsInCollection.value.filter(({ hidden_browse }) => hidden_browse === false)
);
const { sort, limit, page, fields } = useItemOptions();
const { sort, limit, page, fields, fieldsWithRelational } = useItemOptions();
const { items, loading, error, totalPages, itemCount, changeManualSort } = useItems(
collection,
@@ -232,7 +233,7 @@ export default defineComponent({
sort,
limit,
page,
fields,
fields: fieldsWithRelational,
filters: _filters,
searchQuery,
}
@@ -348,10 +349,11 @@ export default defineComponent({
}
}
return (
const fields =
_viewQuery.value?.fields ||
availableFields.value.slice(0, 4).map(({ field }) => field)
);
availableFields.value.slice(0, 4).map(({ field }) => field);
return fields;
},
set(newFields: string[]) {
_viewQuery.value = {
@@ -361,7 +363,11 @@ export default defineComponent({
},
});
return { sort, limit, page, fields };
const fieldsWithRelational = computed(() =>
adjustFieldsForDisplays(fields.value, props.collection)
);
return { sort, limit, page, fields, fieldsWithRelational };
}
function useTable() {

View File

@@ -0,0 +1,32 @@
import useFieldsStore from '@/stores/fields';
import displays from '@/displays';
export default function adjustFieldsForDisplays(
fields: readonly string[],
parentCollection: string
) {
const fieldsStore = useFieldsStore();
const adjustedFields = fields
.map((fieldKey) => {
const field = fieldsStore.getField(parentCollection, fieldKey);
if (!field) return fieldKey;
if (field.display === null) return fieldKey;
const display = displays.find((d) => d.id === field.display);
if (!display?.fields) return fieldKey;
if (Array.isArray(display.fields)) {
return display.fields.map(
(relatedFieldKey: string) => `${fieldKey}.${relatedFieldKey}`
);
}
return fieldKey;
})
.flat();
return adjustedFields;
}

View File

@@ -0,0 +1,4 @@
import adjustFieldsForDisplays from './adjust-fields-for-displays';
export { adjustFieldsForDisplays };
export default adjustFieldsForDisplays;