mirror of
https://github.com/directus/directus.git
synced 2026-04-03 03:00:39 -04:00
@@ -28,7 +28,10 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #append>
|
||||
<v-icon class="deselect" name="close" v-if="file" @click.stop="$emit('input', null)" />
|
||||
<template v-if="file">
|
||||
<v-icon name="open_in_new" class="edit" v-tooltip="$t('edit')" @click.stop="editDrawerActive = true" />
|
||||
<v-icon class="deselect" name="close" @click.stop="$emit('input', null)" v-tooltip="$t('deselect')" />
|
||||
</template>
|
||||
<v-icon v-else name="attach_file" />
|
||||
</template>
|
||||
</v-input>
|
||||
@@ -67,6 +70,15 @@
|
||||
</v-list>
|
||||
</v-menu>
|
||||
|
||||
<drawer-item
|
||||
v-if="!disabled && file"
|
||||
:active.sync="editDrawerActive"
|
||||
collection="directus_files"
|
||||
:primary-key="file.id"
|
||||
:edits="edits"
|
||||
@input="stageEdits"
|
||||
/>
|
||||
|
||||
<v-dialog :active="activeDialog === 'upload'" @esc="activeDialog = null" @toggle="activeDialog = null">
|
||||
<v-card>
|
||||
<v-card-title>{{ $t('upload_from_device') }}</v-card-title>
|
||||
@@ -118,18 +130,19 @@ import readableMimeType from '@/utils/readable-mime-type';
|
||||
import { getRootPath } from '@/utils/get-root-path';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
import { addTokenToURL } from '@/api';
|
||||
import DrawerItem from '../../views/private/components/drawer-item';
|
||||
|
||||
type FileInfo = {
|
||||
id: number;
|
||||
id: string;
|
||||
title: string;
|
||||
type: string;
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
components: { DrawerCollection },
|
||||
components: { DrawerCollection, DrawerItem },
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
type: [String, Object],
|
||||
default: null,
|
||||
},
|
||||
disabled: {
|
||||
@@ -148,7 +161,10 @@ export default defineComponent({
|
||||
return readableMimeType(file.value.type, true);
|
||||
});
|
||||
|
||||
const assetURL = computed(() => getRootPath() + `assets/${props.value}`);
|
||||
const assetURL = computed(() => {
|
||||
const id = typeof props.value === 'string' ? props.value : (props.value as Record<string, any>)?.id;
|
||||
return getRootPath() + `assets/${id}`;
|
||||
});
|
||||
|
||||
const imageThumbnail = computed(() => {
|
||||
if (file.value === null || props.value === null) return null;
|
||||
@@ -158,8 +174,11 @@ export default defineComponent({
|
||||
return addTokenToURL(url);
|
||||
});
|
||||
|
||||
const { edits, stageEdits } = useEdits();
|
||||
const { url, isValidURL, loading: urlLoading, importFromURL } = useURLImport();
|
||||
|
||||
const editDrawerActive = ref(false);
|
||||
|
||||
return {
|
||||
activeDialog,
|
||||
setSelection,
|
||||
@@ -173,6 +192,9 @@ export default defineComponent({
|
||||
importFromURL,
|
||||
isValidURL,
|
||||
assetURL,
|
||||
editDrawerActive,
|
||||
edits,
|
||||
stageEdits,
|
||||
};
|
||||
|
||||
function useFile() {
|
||||
@@ -191,13 +213,22 @@ export default defineComponent({
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
const response = await api.get(`/files/${props.value}`, {
|
||||
const id = typeof props.value === 'string' ? props.value : (props.value as Record<string, any>)?.id;
|
||||
|
||||
const response = await api.get(`/files/${id}`, {
|
||||
params: {
|
||||
fields: ['title', 'type', 'filename_download'],
|
||||
fields: ['id', 'title', 'type', 'filename_download'],
|
||||
},
|
||||
});
|
||||
|
||||
file.value = response.data.data;
|
||||
if (props.value !== null && typeof props.value === 'object') {
|
||||
file.value = {
|
||||
...response.data.data,
|
||||
...props.value,
|
||||
};
|
||||
} else {
|
||||
file.value = response.data.data;
|
||||
}
|
||||
} catch (err) {
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
@@ -255,6 +286,29 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function useEdits() {
|
||||
const edits = computed(() => {
|
||||
// If the current value isn't a primitive, it means we've already staged some changes
|
||||
// This ensures we continue on those changes instead of starting over
|
||||
if (props.value && typeof props.value === 'object') {
|
||||
return props.value;
|
||||
}
|
||||
|
||||
return {};
|
||||
});
|
||||
|
||||
return { edits, stageEdits };
|
||||
|
||||
function stageEdits(newEdits: Record<string, any>) {
|
||||
if (!file.value) return;
|
||||
|
||||
emit('input', {
|
||||
id: file.value.id,
|
||||
...newEdits,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -304,4 +358,12 @@ export default defineComponent({
|
||||
.deselect:hover {
|
||||
--v-icon-color: var(--danger);
|
||||
}
|
||||
|
||||
.edit {
|
||||
margin-right: 4px;
|
||||
|
||||
&:hover {
|
||||
--v-icon-color: var(--foreground-normal);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
<v-button icon rounded :href="downloadSrc" :download="image.filename_download" v-tooltip="$t('download')">
|
||||
<v-icon name="get_app" />
|
||||
</v-button>
|
||||
<v-button icon rounded @click="editorActive = true" v-tooltip="$t('edit')">
|
||||
<v-icon name="crop_rotate" />
|
||||
<v-button icon rounded @click="editDrawerActive = true" v-tooltip="$t('edit')">
|
||||
<v-icon name="open_in_new" />
|
||||
</v-button>
|
||||
<v-button icon rounded @click="deselect" v-tooltip="$t('deselect')">
|
||||
<v-icon name="close" />
|
||||
@@ -31,12 +31,15 @@
|
||||
<div class="meta">{{ meta }}</div>
|
||||
</div>
|
||||
|
||||
<image-editor
|
||||
v-if="image && image.type.startsWith('image')"
|
||||
:id="image.id"
|
||||
@refresh="changeCacheBuster"
|
||||
v-model="editorActive"
|
||||
<drawer-item
|
||||
v-if="!disabled && image"
|
||||
:active.sync="editDrawerActive"
|
||||
collection="directus_files"
|
||||
:primary-key="image.id"
|
||||
:edits="edits"
|
||||
@input="stageEdits"
|
||||
/>
|
||||
|
||||
<file-lightbox v-model="lightboxActive" :id="image.id" />
|
||||
</div>
|
||||
<v-upload v-else @input="setImage" from-library from-url />
|
||||
@@ -54,6 +57,7 @@ import { nanoid } from 'nanoid';
|
||||
import { getRootPath } from '@/utils/get-root-path';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
import { addTokenToURL } from '@/api';
|
||||
import DrawerItem from '../../views/private/components/drawer-item';
|
||||
|
||||
type Image = {
|
||||
id: string; // uuid
|
||||
@@ -65,10 +69,10 @@ type Image = {
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
components: { FileLightbox, ImageEditor },
|
||||
components: { FileLightbox, ImageEditor, DrawerItem },
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
type: [String, Object],
|
||||
default: null,
|
||||
},
|
||||
disabled: {
|
||||
@@ -80,7 +84,7 @@ export default defineComponent({
|
||||
const loading = ref(false);
|
||||
const image = ref<Image | null>(null);
|
||||
const lightboxActive = ref(false);
|
||||
const editorActive = ref(false);
|
||||
const editDrawerActive = ref(false);
|
||||
|
||||
const cacheBuster = ref(nanoid());
|
||||
|
||||
@@ -114,43 +118,56 @@ export default defineComponent({
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
(newID, oldID) => {
|
||||
if (newID === oldID) return;
|
||||
(newValue, oldValue) => {
|
||||
if (newValue === oldValue) return;
|
||||
|
||||
if (newID) {
|
||||
if (newValue) {
|
||||
fetchImage();
|
||||
}
|
||||
|
||||
if (oldID && newID === null) {
|
||||
if (oldValue && newValue === null) {
|
||||
deselect();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const { edits, stageEdits } = useEdits();
|
||||
|
||||
return {
|
||||
loading,
|
||||
image,
|
||||
src,
|
||||
meta,
|
||||
lightboxActive,
|
||||
editorActive,
|
||||
editDrawerActive,
|
||||
changeCacheBuster,
|
||||
setImage,
|
||||
deselect,
|
||||
downloadSrc,
|
||||
edits,
|
||||
stageEdits,
|
||||
};
|
||||
|
||||
async function fetchImage() {
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
const response = await api.get(`/files/${props.value}`, {
|
||||
const id = typeof props.value === 'string' ? props.value : (props.value as Record<string, any>)?.id;
|
||||
|
||||
const response = await api.get(`/files/${id}`, {
|
||||
params: {
|
||||
fields: ['id', 'title', 'width', 'height', 'filesize', 'type', 'filename_download'],
|
||||
},
|
||||
});
|
||||
|
||||
image.value = response.data.data;
|
||||
if (props.value !== null && typeof props.value === 'object') {
|
||||
image.value = {
|
||||
...response.data.data,
|
||||
...props.value,
|
||||
};
|
||||
} else {
|
||||
image.value = response.data.data;
|
||||
}
|
||||
} catch (err) {
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
@@ -173,7 +190,30 @@ export default defineComponent({
|
||||
loading.value = false;
|
||||
image.value = null;
|
||||
lightboxActive.value = false;
|
||||
editorActive.value = false;
|
||||
editDrawerActive.value = false;
|
||||
}
|
||||
|
||||
function useEdits() {
|
||||
const edits = computed(() => {
|
||||
// If the current value isn't a primitive, it means we've already staged some changes
|
||||
// This ensures we continue on those changes instead of starting over
|
||||
if (props.value && typeof props.value === 'object') {
|
||||
return props.value;
|
||||
}
|
||||
|
||||
return {};
|
||||
});
|
||||
|
||||
return { edits, stageEdits };
|
||||
|
||||
function stageEdits(newEdits: Record<string, any>) {
|
||||
if (!image.value) return;
|
||||
|
||||
emit('input', {
|
||||
id: image.value.id,
|
||||
...newEdits,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -106,9 +106,8 @@ export default defineComponent({
|
||||
|
||||
const showDivider = computed(() => {
|
||||
return (
|
||||
fieldsStore
|
||||
.getFieldsForCollection(props.collection)
|
||||
.filter((field: Field) => field.meta?.hidden !== true).length > 0
|
||||
fieldsStore.getFieldsForCollection(props.collection).filter((field: Field) => field.meta?.hidden !== true)
|
||||
.length > 0
|
||||
);
|
||||
});
|
||||
|
||||
@@ -243,9 +242,7 @@ export default defineComponent({
|
||||
const relations = relationsStore.getRelationsForField(props.collection, props.junctionField);
|
||||
|
||||
const relationForField = relations.find((relation: Relation) => {
|
||||
return (
|
||||
relation.many_collection === props.collection && relation.many_field === props.junctionField
|
||||
);
|
||||
return relation.many_collection === props.collection && relation.many_field === props.junctionField;
|
||||
});
|
||||
|
||||
if (relationForField.one_collection) return relationForField.one_collection;
|
||||
|
||||
Reference in New Issue
Block a user