Persist open state of folders + add root file library

Fixes #110, fixes #111
This commit is contained in:
rijkvanzanten
2020-08-12 09:50:19 -04:00
parent 1ef278c717
commit 96e8e9aec8
6 changed files with 101 additions and 62 deletions

View File

@@ -8,11 +8,11 @@
</v-list-item-icon>
</v-list-item>
<transition-expand>
<div class="items" v-show="groupActive">
<slot />
</div>
</transition-expand>
<!-- <transition-expand> -->
<div class="items" v-show="groupActive">
<slot />
</div>
<!-- </transition-expand> -->
</div>
</template>
@@ -42,24 +42,33 @@ export default defineComponent({
type: Boolean,
default: false,
},
open: {
disableGroupableParent: {
type: Boolean,
default: false,
},
scope: {
type: String,
default: undefined,
},
value: {
type: [String, Number],
default: undefined,
},
},
setup(props, { listeners, emit }) {
const { active: groupActive, toggle, activate, deactivate } = useGroupable({
active: toRefs(props).open,
group: props.scope,
value: props.value,
});
// watch(() => props.open, () => props.open ? activate() : deactivate(), { immediate: true });
useGroupableParent(
{},
{
multiple: toRefs(props).multiple,
}
);
if (props.disableGroupableParent !== true) {
useGroupableParent(
{},
{
multiple: toRefs(props).multiple,
}
);
}
return { groupActive, toggle, onClick };
@@ -83,9 +92,9 @@ export default defineComponent({
}
.activator-icon {
color: var(--foreground-subdued);
transform: rotate(0deg);
transition: transform var(--medium) var(--transition);
color: var(--foreground-subdued);
&:hover {
color: var(--foreground-normal);

View File

@@ -15,6 +15,7 @@ type GroupableOptions = {
value?: string | number;
group?: string;
active?: Ref<boolean>;
watch?: boolean;
};
export function useGroupable(options?: GroupableOptions) {
@@ -43,14 +44,12 @@ export function useGroupable(options?: GroupableOptions) {
toggle: (item: GroupableInstance) => void;
} = parentFunctions;
const active = ref(false);
const active = ref(options?.active?.value === true ? true : false);
const item = { active, value: options?.value };
register(item);
if (options?.active) {
if (options.active.value === true) toggle(item);
if (options?.active !== undefined && options.watch === true) {
watch(options.active, () => {
if (options.active === undefined) return;

View File

@@ -7,7 +7,7 @@
exact
@contextmenu.native.prevent.stop="$refs.contextMenu.activate"
>
<v-list-item-icon><v-icon name="folder" /></v-list-item-icon>
<v-list-item-icon><v-icon :name="root ? 'folder_special' : 'folder'" /></v-list-item-icon>
<v-list-item-content>{{ folder.name }}</v-list-item-content>
</v-list-item>
@@ -16,22 +16,24 @@
:to="`/files?folder=${folder.id}`"
:active="currentFolder === folder.id"
exact
:open="isOpen"
@contextmenu.native.prevent.stop="$refs.contextMenu.activate"
scope="files-navigation"
:value="root ? '$root' : folder.id"
disable-groupable-parent
>
<template #activator>
<v-list-item-icon>
<v-icon name="folder" />
<v-icon :name="root ? 'folder_special' : '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"
:start-open-folders="startOpenFolders"
/>
</v-list-group>
@@ -131,9 +133,9 @@ export default defineComponent({
type: Function,
default: () => undefined,
},
startOpenFolders: {
type: Array as PropType<string[]>,
default: () => [],
root: {
type: Boolean,
default: false,
},
},
setup(props) {
@@ -143,10 +145,6 @@ export default defineComponent({
const { fetchFolders } = useFolders();
const isOpen = computed(() => {
return props.startOpenFolders.includes(props.folder.id);
});
return {
renameActive,
renameValue,
@@ -159,7 +157,6 @@ export default defineComponent({
deleteActive,
deleteSave,
deleteSaving,
isOpen,
};
function useRenameFolder() {

View File

@@ -1,12 +1,5 @@
<template>
<v-list nav>
<v-list-item to="/files/" exact>
<v-list-item-icon><v-icon name="folder_special" /></v-list-item-icon>
<v-list-item-content>{{ $t('all_files') }}</v-list-item-content>
</v-list-item>
<v-divider v-if="loading || nestedFolders.length > 0" />
<template v-if="loading && (nestedFolders === null || nestedFolders.length === 0)">
<v-list-item v-for="n in 4" :key="n">
<v-skeleton-loader type="list-item-icon" />
@@ -14,21 +7,31 @@
</template>
<div class="folders">
<navigation-folder
v-for="folder in nestedFolders"
:key="folder.id"
:folder="folder"
:current-folder="currentFolder"
:start-open-folders="startOpenFolders"
/>
<v-item-group scope="files-navigation" multiple v-model="openFolders">
<navigation-folder
v-for="folder in nestedFolders"
:key="folder.id"
:folder="folder"
:current-folder="currentFolder"
root
/>
</v-item-group>
</div>
<v-divider v-if="loading || nestedFolders.length > 0" />
<v-list-item to="/files/" exact>
<v-list-item-icon><v-icon name="folder_special" /></v-list-item-icon>
<v-list-item-content>{{ $t('all_files') }}</v-list-item-content>
</v-list-item>
</v-list>
</template>
<script lang="ts">
import { defineComponent, computed } from '@vue/composition-api';
import { defineComponent, computed, ref, watch } from '@vue/composition-api';
import useFolders from '../../composables/use-folders';
import NavigationFolder from './navigation-folder.vue';
import arraysAreEqual from '@/utils/arrays-are-equal';
export default defineComponent({
components: { NavigationFolder },
@@ -43,21 +46,38 @@ export default defineComponent({
},
},
setup(props) {
const { nestedFolders, folders, error, loading } = useFolders();
const { nestedFolders, folders, error, loading, openFolders } = useFolders();
const startOpenFolders = computed(() => {
setOpenFolders();
watch(() => props.currentFolder, setOpenFolders);
return { folders, nestedFolders, error, loading, openFolders };
function setOpenFolders() {
if (!folders.value) return [];
if (!openFolders?.value) return [];
const openFolders: string[] = [];
const shouldBeOpen: string[] = [];
const folder = folders.value.find((folder) => folder.id === props.currentFolder);
if (folder && folder.parent_folder) parseFolder(folder.parent_folder);
return openFolders;
const newOpenFolders = [...openFolders.value];
for (const folderID of shouldBeOpen) {
if (newOpenFolders.includes(folderID) === false) {
newOpenFolders.push(folderID);
}
}
if (newOpenFolders.length !== 1 && arraysAreEqual(newOpenFolders, openFolders.value) === false) {
openFolders.value = newOpenFolders;
}
function parseFolder(id: string) {
if (!folders.value) return;
openFolders.push(id);
shouldBeOpen.push(id);
const folder = folders.value.find((folder) => folder.id === id);
@@ -65,9 +85,7 @@ export default defineComponent({
parseFolder(folder.parent_folder);
}
}
});
return { folders, nestedFolders, error, loading, startOpenFolders };
}
},
});
</script>

View File

@@ -1,22 +1,25 @@
import api from '@/api';
import i18n from '@/lang';
import { ref, Ref } from '@vue/composition-api';
import { TranslateResult } from 'vue-i18n';
type FolderRaw = {
id: string;
name: string;
parent_folder: string;
parent_folder: string | null;
};
export type Folder = {
id: string;
name: string;
parent_folder: string;
id: string | null;
name: string | TranslateResult;
parent_folder: string | null;
children?: Folder[];
};
let loading: Ref<boolean> | null = null;
let folders: Ref<Folder[] | null> | null = null;
let nestedFolders: Ref<Folder[] | null> | null = null;
let openFolders: Ref<string[] | null> | null = null;
let error: Ref<any> | null = null;
@@ -25,12 +28,13 @@ export default function useFolders() {
if (folders === null) folders = ref<Folder[] | null>(null);
if (nestedFolders === null) nestedFolders = ref<Folder[] | null>(null);
if (error === null) error = ref(null);
if (openFolders === null) openFolders = ref(['$root']);
if (folders.value === null && loading.value === false) {
fetchFolders();
}
return { loading, folders, nestedFolders, error, fetchFolders };
return { loading, folders, nestedFolders, error, fetchFolders, openFolders };
async function fetchFolders() {
if (loading === null) return;
@@ -49,7 +53,14 @@ export default function useFolders() {
});
folders.value = response.data.data;
nestedFolders.value = nestFolders(response.data.data);
nestedFolders.value = [
{
id: null,
name: i18n.t('file_library'),
children: nestFolders(response.data.data),
parent_folder: null,
},
];
} catch (err) {
error.value = err;
} finally {

View File

@@ -334,7 +334,12 @@ export default defineComponent({
const breadcrumb = computed(() => [
{
name: i18n.t('file_library'),
to: `/files/`,
to: {
path: `/files/`,
query: {
folder: item?.value?.folder,
},
},
},
]);