Merge pull request #106 from directus/folders-ctx

Folders ctx
This commit is contained in:
Rijk van Zanten
2020-08-11 14:27:42 -04:00
committed by GitHub
27 changed files with 343 additions and 83 deletions

View File

@@ -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>

View File

@@ -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',
},
});

View File

@@ -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>

View File

@@ -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) {

View File

@@ -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" />

View File

@@ -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>

View File

@@ -42,7 +42,7 @@
/>
</draggable>
<v-menu attached close-on-content-click>
<v-menu attached>
<template #activator="{ toggle, active }">
<v-button
@click="toggle"