Add default-folder option (#3209)

* Allow set folder for imported files

* Allow passing folder in file/files component; Allow pick folder for file/files/image interfaces.

* Added folder system component for picking folders; Move folder picker the field from data to interface (file, files, image).

* Add custom folder interface; use props for interfaces file/files/image in upload component

* Allow set folder for imported files

* Allow passing folder in file/files component; Allow pick folder for file/files/image interfaces.

* Added folder system component for picking folders; Move folder picker the field from data to interface (file, files, image).

* Add custom folder interface; use props for interfaces file/files/image in upload component

* Update after rebase

* Add storage_default_folder setting, use folder when deploy file

* Fix files options; Add default label option for folder interface.

* Fix set folder for file

* UX

* Add migration for column, undo seed change

* Update nomanclature

* Make sure file library always submits folder, cleanup setting retrieval

* Use indexName on down migrate

* Fix import default folder, rename customPresets->folderPreset

* Rename interface import

* Use undefined as default folder

* Remove deprecated lang file

* Fix display of folder interface, treat null as value

* Move shared composable

* Remove unused ref

Co-authored-by: rijkvanzanten <rijkvanzanten@me.com>
This commit is contained in:
Adrian Dimitrov
2021-07-22 00:29:21 +03:00
committed by GitHub
parent d487f691a3
commit efe7b076a3
25 changed files with 365 additions and 34 deletions

View File

@@ -0,0 +1,70 @@
<template>
<v-list-item
v-if="!folder.children || folder.children.length === 0"
:active="currentFolder === folder.id"
:disabled="disabled"
clickable
@click="$emit('click', folder.id)"
>
<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
clickable
:active="currentFolder === folder.id"
:disabled="disabled"
@click="$emit('click', folder.id)"
>
<template #activator>
<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>
</template>
<folder-list-item
v-for="childFolder in folder.children"
:key="childFolder.id"
:folder="childFolder"
:current-folder="currentFolder"
:disabled="disabledFolders.includes(childFolder.id)"
:disabled-folders="disabledFolders"
@click="$emit('click', $event)"
/>
</v-list-group>
</template>
<script lang="ts">
import { defineComponent, PropType } from 'vue';
type Folder = {
id: string;
name: string;
children: Folder[];
};
export default defineComponent({
name: 'FolderListItem',
props: {
folder: {
type: Object as PropType<Folder>,
required: true,
},
currentFolder: {
type: String,
default: null,
},
disabled: {
type: Boolean,
default: false,
},
disabledFolders: {
type: Array as PropType<string[]>,
default: () => [],
},
},
emits: ['click'],
});
</script>

View File

@@ -0,0 +1,140 @@
<template>
<v-skeleton-loader v-if="loading" />
<v-menu
v-else
class="v-select"
:attached="true"
:show-arrow="false"
:disabled="disabled"
:close-on-content-click="true"
>
<template #activator="{ toggle, active }">
<v-input
readonly
:active="active"
:model-value="folderPath"
:placeholder="placeholder"
:disabled="disabled"
@click="toggle"
>
<template #prepend><v-icon :name="!value ? 'folder_special' : 'folder_open'" /></template>
<template #append><v-icon name="expand_more" :class="{ active }" /></template>
</v-input>
</template>
<v-list>
<v-list-item clickable :active="!value" @click="emitValue(null)">
<v-list-item-icon>
<v-icon name="folder_special" />
</v-list-item-icon>
<v-list-item-content>{{ t('interfaces.system-folder.root_name') }}</v-list-item-content>
</v-list-item>
<v-divider v-if="nestedFolders && nestedFolders.length > 0" />
<folder-list-item
v-for="folder in nestedFolders"
:key="folder.id"
clickbable
:folder="folder"
:current-folder="value"
:disabled="disabledFolders.includes(folder.id)"
:disabled-folders="disabledFolders"
@click="emitValue"
/>
</v-list>
</v-menu>
</template>
<script lang="ts">
import { computed, defineComponent, PropType, ref } from 'vue';
import FolderListItem from './folder-list-item.vue';
import useFolders, { Folder } from '@/composables/use-folders';
import { useI18n } from 'vue-i18n';
export default defineComponent({
components: { FolderListItem },
props: {
value: {
type: String,
default: undefined,
},
disabledFolders: {
type: Array as PropType<string[]>,
default: () => [],
},
disabled: {
type: Boolean,
default: false,
},
placeholder: {
type: String,
default: '',
},
},
emits: ['input'],
setup(props, { emit }) {
const { t } = useI18n();
const { nestedFolders, folders, loading } = useFolders();
const folderPath = computed(() => {
if (!props.value || !folders.value) {
return t('interfaces.system-folder.root_name');
}
const folder = folders.value.find((folder) => folder.id === props.value);
return folder
? folderParentPath(folder as Folder, folders.value)
.map((folder) => folder.name)
.join(' / ')
: props.value;
});
return {
emitValue,
loading,
folderPath,
nestedFolders,
onFolderSelect,
t,
};
function emitValue(id: string | null) {
return emit('input', id);
}
function folderParentPath(folder: Folder, folders: Folder[]) {
const folderMap = new Map(folders.map((folder) => [folder.id, folder]));
const folderParent = (target: Folder): Folder[] =>
(folderMap.has(target.parent) ? folderParent(folderMap.get(target.parent) as Folder) : []).concat(target);
return folderParent(folder);
}
function onFolderSelect(folderId: string | null) {
if (props.disabled) {
return;
}
emit('input', folderId);
}
},
});
</script>
<style lang="scss" scoped>
.v-input {
cursor: pointer;
.v-icon {
transition: transform var(--medium) var(--transition-out);
&.active {
transform: scaleY(-1);
transition-timing-function: var(--transition-in);
}
}
:deep(input) {
cursor: pointer;
}
}
</style>

View File

@@ -0,0 +1,14 @@
import { defineInterface } from '@/interfaces/define';
import InterfaceSystemFolder from './folder.vue';
export default defineInterface({
id: 'system-folder',
name: '$t:interfaces.system-folder.folder',
description: '$t:interfaces.system-folder.description',
icon: 'folder',
component: InterfaceSystemFolder,
types: ['uuid'],
options: [],
system: true,
recommendedDisplays: ['raw'],
});