mirror of
https://github.com/directus/directus.git
synced 2026-02-04 05:25:04 -05:00
@@ -3,11 +3,12 @@
|
||||
v-if="folder.children.length === 0"
|
||||
@click="clickHandler(folder.id)"
|
||||
:active="currentFolder === folder.id"
|
||||
:disabled="disabled"
|
||||
>
|
||||
<v-list-item-icon><v-icon :name="currentFolder === folder.id ? 'folder_open' : 'folder'" /></v-list-item-icon>
|
||||
<v-list-item-content>{{ folder.name }}</v-list-item-content>
|
||||
</v-list-item>
|
||||
<v-list-group v-else @click="clickHandler(folder.id)" :active="currentFolder === folder.id">
|
||||
<v-list-group v-else @click="clickHandler(folder.id)" :active="currentFolder === folder.id" :disabled="disabled">
|
||||
<template #activator>
|
||||
<v-list-item-icon>
|
||||
<v-icon :name="currentFolder === folder.id ? 'folder_open' : 'folder'" />
|
||||
@@ -20,6 +21,8 @@
|
||||
:folder="childFolder"
|
||||
:current-folder="currentFolder"
|
||||
:click-handler="clickHandler"
|
||||
:disabled="disabledFolders.includes(childFolder.id)"
|
||||
:disabled-folders="disabledFolders"
|
||||
/>
|
||||
</v-list-group>
|
||||
</template>
|
||||
@@ -48,6 +51,14 @@ export default defineComponent({
|
||||
type: Function,
|
||||
default: () => undefined,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
disabledFolders: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
:folder="folder"
|
||||
:current-folder="value"
|
||||
:click-handler="(id) => $emit('input', id)"
|
||||
:disabled="disabledFolders.includes(folder.id)"
|
||||
:disabled-folders="disabledFolders"
|
||||
/>
|
||||
</v-list-group>
|
||||
</v-list>
|
||||
@@ -23,7 +25,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, computed } from '@vue/composition-api';
|
||||
import { defineComponent, ref, computed, PropType } from '@vue/composition-api';
|
||||
import api from '@/api';
|
||||
import FolderPickerListItem from './folder-picker-list-item.vue';
|
||||
|
||||
@@ -42,6 +44,10 @@ type Folder = {
|
||||
export default defineComponent({
|
||||
components: { FolderPickerListItem },
|
||||
props: {
|
||||
disabledFolders: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => [],
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: null,
|
||||
@@ -91,6 +97,7 @@ export default defineComponent({
|
||||
const response = await api.get(`/folders`, {
|
||||
params: {
|
||||
limit: -1,
|
||||
sort: 'name',
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,35 +1,118 @@
|
||||
<template>
|
||||
<v-list-item
|
||||
v-if="folder.children === undefined"
|
||||
:to="`/files?folder=${folder.id}`"
|
||||
exact
|
||||
>
|
||||
<v-list-item-icon><v-icon name="folder" /></v-list-item-icon>
|
||||
<v-list-item-content>{{ folder.name }}</v-list-item-content>
|
||||
</v-list-item>
|
||||
<v-list-group v-else @click="clickHandler(folder.id)" :active="currentFolder === folder.id">
|
||||
<template #activator="{ active }">
|
||||
<v-list-item-icon>
|
||||
<v-icon :name="active ? 'folder_open' : 'folder'" />
|
||||
</v-list-item-icon>
|
||||
<div>
|
||||
<v-list-item
|
||||
v-if="folder.children === undefined"
|
||||
:to="`/files?folder=${folder.id}`"
|
||||
exact
|
||||
@contextmenu.native.prevent.stop="$refs.contextMenu.activate"
|
||||
>
|
||||
<v-list-item-icon><v-icon name="folder" /></v-list-item-icon>
|
||||
<v-list-item-content>{{ folder.name }}</v-list-item-content>
|
||||
</template>
|
||||
<navigation-folder
|
||||
v-for="childFolder in folder.children"
|
||||
:key="childFolder.id"
|
||||
:folder="childFolder"
|
||||
:current-folder="currentFolder"
|
||||
:click-handler="clickHandler"
|
||||
/>
|
||||
</v-list-group>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-group
|
||||
v-else
|
||||
:to="`/files?folder=${folder.id}`"
|
||||
exact
|
||||
@contextmenu.native.prevent="$refs.contextMenu.activate"
|
||||
>
|
||||
<template #activator="{ active }">
|
||||
<v-list-item-icon>
|
||||
<v-icon :name="active ? 'folder_open' : 'folder'" />
|
||||
</v-list-item-icon>
|
||||
<v-list-item-content>{{ folder.name }}</v-list-item-content>
|
||||
</template>
|
||||
<navigation-folder
|
||||
v-for="childFolder in folder.children"
|
||||
:key="childFolder.id"
|
||||
:folder="childFolder"
|
||||
:current-folder="currentFolder"
|
||||
:click-handler="clickHandler"
|
||||
/>
|
||||
</v-list-group>
|
||||
|
||||
<v-menu ref="contextMenu" show-arrow placement="bottom-start">
|
||||
<v-list dense>
|
||||
<v-list-item @click="renameActive = true">
|
||||
<v-list-item-icon>
|
||||
<v-icon name="edit" />
|
||||
</v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
{{ $t('rename_folder') }}
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
<v-list-item @click="moveActive = true">
|
||||
<v-list-item-icon>
|
||||
<v-icon name="folder_move" />
|
||||
</v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
{{ $t('move_to_folder') }}
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
<v-list-item @click="deleteActive = true">
|
||||
<v-list-item-icon>
|
||||
<v-icon name="delete" />
|
||||
</v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
{{ $t('delete_folder') }}
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
|
||||
<v-dialog v-model="renameActive" persistent>
|
||||
<v-card>
|
||||
<v-card-title>{{ $t('rename_folder') }}</v-card-title>
|
||||
<v-card-text>
|
||||
<v-input v-model="renameValue" />
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-button secondary @click="renameActive = false">{{ $t('cancel') }}</v-button>
|
||||
<v-button @click="renameSave" :loading="renameSaving">{{ $t('save') }}</v-button>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<v-dialog v-model="moveActive" persistent>
|
||||
<v-card>
|
||||
<v-card-title>{{ $t('move_to_folder') }}</v-card-title>
|
||||
<v-card-text>
|
||||
<folder-picker v-model="moveValue" :disabled-folders="[folder.id]" />
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-button secondary @click="moveActive = false">{{ $t('cancel') }}</v-button>
|
||||
<v-button @click="moveSave" :loading="moveSaving">{{ $t('save') }}</v-button>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<v-dialog v-model="deleteActive" persistent>
|
||||
<v-card>
|
||||
<v-card-title>{{ $t('delete_folder') }}</v-card-title>
|
||||
<v-card-text>
|
||||
<v-notice>
|
||||
{{ $t('nested_files_folders_will_be_moved') }}
|
||||
</v-notice>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-button secondary @click="deleteActive = false">{{ $t('cancel') }}</v-button>
|
||||
<v-button @click="deleteSave" :loading="deleteSaving">{{ $t('delete') }}</v-button>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from '@vue/composition-api';
|
||||
import { Folder } from '../../composables/use-folders';
|
||||
import { defineComponent, PropType, ref } from '@vue/composition-api';
|
||||
import useFolders, { Folder } from '../../composables/use-folders';
|
||||
import notify from '@/utils/notify';
|
||||
import api from '@/api';
|
||||
import FolderPicker from '../folder-picker';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'navigation-folder',
|
||||
components: { FolderPicker },
|
||||
props: {
|
||||
folder: {
|
||||
type: Object as PropType<Folder>,
|
||||
@@ -44,5 +127,129 @@ export default defineComponent({
|
||||
default: () => undefined,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const { renameActive, renameValue, renameSave, renameSaving } = useRenameFolder();
|
||||
const { moveActive, moveValue, moveSave, moveSaving } = useMoveFolder();
|
||||
const { deleteActive, deleteSave, deleteSaving } = useDeleteFolder();
|
||||
|
||||
const { fetchFolders } = useFolders();
|
||||
|
||||
return {
|
||||
renameActive,
|
||||
renameValue,
|
||||
renameSave,
|
||||
renameSaving,
|
||||
moveActive,
|
||||
moveValue,
|
||||
moveSave,
|
||||
moveSaving,
|
||||
deleteActive,
|
||||
deleteSave,
|
||||
deleteSaving,
|
||||
};
|
||||
|
||||
function useRenameFolder() {
|
||||
const renameActive = ref(false);
|
||||
const renameValue = ref(props.folder.name);
|
||||
const renameSaving = ref(false);
|
||||
|
||||
return { renameActive, renameValue, renameSave, renameSaving };
|
||||
|
||||
async function renameSave() {
|
||||
renameSaving.value = true;
|
||||
|
||||
try {
|
||||
await api.patch(`/folders/${props.folder.id}`, {
|
||||
name: renameValue.value,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} finally {
|
||||
renameSaving.value = false;
|
||||
await fetchFolders();
|
||||
renameActive.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function useMoveFolder() {
|
||||
const moveActive = ref(false);
|
||||
const moveValue = ref(props.folder.parent_folder);
|
||||
const moveSaving = ref(false);
|
||||
|
||||
return { moveActive, moveValue, moveSave, moveSaving };
|
||||
|
||||
async function moveSave() {
|
||||
moveSaving.value = true;
|
||||
|
||||
try {
|
||||
await api.patch(`/folders/${props.folder.id}`, {
|
||||
parent_folder: moveValue.value,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} finally {
|
||||
moveSaving.value = false;
|
||||
await fetchFolders();
|
||||
moveActive.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function useDeleteFolder() {
|
||||
const deleteActive = ref(false);
|
||||
const deleteSaving = ref(false);
|
||||
|
||||
return { deleteActive, deleteSave, deleteSaving };
|
||||
|
||||
async function deleteSave() {
|
||||
deleteSaving.value = true;
|
||||
|
||||
try {
|
||||
const foldersToUpdate = await api.get('/folders', {
|
||||
params: {
|
||||
filter: {
|
||||
parent_folder: {
|
||||
_eq: props.folder.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const filesToUpdate = await api.get('/files', {
|
||||
params: {
|
||||
filter: {
|
||||
folder: {
|
||||
_eq: props.folder.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const newParent = props.folder.parent_folder || null;
|
||||
|
||||
const folderKeys = foldersToUpdate.data.data.map((folder: { id: string }) => folder.id);
|
||||
const fileKeys = filesToUpdate.data.data.map((file: { id: string }) => file.id);
|
||||
|
||||
if (folderKeys.length > 0) {
|
||||
await api.patch(`/folders/${folderKeys.join(',')}`, { parent_folder: newParent });
|
||||
}
|
||||
|
||||
if (fileKeys.length > 0) {
|
||||
await api.patch(`/files/${fileKeys.join(',')}`, { folder: newParent });
|
||||
}
|
||||
|
||||
await api.delete(`/folders/${props.folder.id}`);
|
||||
|
||||
deleteActive.value = false;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} finally {
|
||||
await fetchFolders();
|
||||
deleteSaving.value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -2,14 +2,15 @@ import api from '@/api';
|
||||
import { ref, Ref } from '@vue/composition-api';
|
||||
|
||||
type FolderRaw = {
|
||||
id: number;
|
||||
id: string;
|
||||
name: string;
|
||||
parent_folder: number;
|
||||
parent_folder: string;
|
||||
};
|
||||
|
||||
export type Folder = {
|
||||
id: number;
|
||||
id: string;
|
||||
name: string;
|
||||
parent_folder: string;
|
||||
children?: Folder[];
|
||||
};
|
||||
|
||||
@@ -63,7 +64,7 @@ export function nestChildren(rawFolder: FolderRaw, rawFolders: FolderRaw[]) {
|
||||
const folder: FolderRaw & Folder = { ...rawFolder };
|
||||
|
||||
const children = rawFolders
|
||||
.filter((childFolder) => childFolder.parent_folder === rawFolder.id)
|
||||
.filter((childFolder) => childFolder.parent_folder === rawFolder.id && childFolder.id !== rawFolder.id)
|
||||
.map((childRawFolder) => nestChildren(childRawFolder, rawFolders));
|
||||
|
||||
if (children.length > 0) {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</v-button>
|
||||
|
||||
<div v-else-if="collection.collection.startsWith('directus_') === false">
|
||||
<v-menu placement="left-start" show-arrow close-on-content-click :disabled="savingManaged">
|
||||
<v-menu placement="left-start" show-arrow :disabled="savingManaged">
|
||||
<template #activator="{ toggle }">
|
||||
<v-progress-circular small v-if="savingManaged" indeterminate />
|
||||
<v-icon v-else name="more_vert" @click="toggle" class="ctx-toggle" />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div :class="field.meta.width || 'full'">
|
||||
<v-menu attached close-on-content-click>
|
||||
<v-menu attached>
|
||||
<template #activator="{ toggle, active }">
|
||||
<v-input class="field" :class="{ hidden, active }" readonly @click="toggle">
|
||||
<template #prepend>
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
/>
|
||||
</draggable>
|
||||
|
||||
<v-menu attached close-on-content-click>
|
||||
<v-menu attached>
|
||||
<template #activator="{ toggle, active }">
|
||||
<v-button
|
||||
@click="toggle"
|
||||
|
||||
Reference in New Issue
Block a user