Version 1.3

- i18n localization support
- Added SK, EN language files
- Video/Audio preview in file preview panel (Thanks to Joshua Fouyon to participating on this feature)
- Drop uploading (You can now drag files from desktop and drop it to VueFileManager)
- Fixed bug when rename item in safari browser
- Fixed bug when you drag folder from trash to favourites in sidebar panel
- small functions and design improvements
This commit is contained in:
MakingCG
2020-04-03 11:52:54 +02:00
parent 96da39923d
commit 4504276563
28 changed files with 1558 additions and 936 deletions

View File

@@ -8,7 +8,7 @@
<ul class="menu-options" id="menu-options-list" ref="list" @click="closeAndResetContextMenu">
<!--View-->
<li class="menu-option" @click="addToFavourites" v-if="! $isTrashLocation() && item && item.type === 'folder'">{{ isInFavourites ? $t('context_menu.remove_from_favourites') : $t('context_menu.add_to_favourites') }}</li>
<li class="menu-option" @click="addToFavourites" v-if="! $isTrashLocation() && item && isFolder">{{ isInFavourites ? $t('context_menu.remove_from_favourites') : $t('context_menu.add_to_favourites') }}</li>
<li class="menu-option" @click="createFolder" v-if="! $isTrashLocation()">{{ $t('context_menu.create_folder') }}</li>
<!--Edits-->
@@ -21,7 +21,7 @@
<!--Others-->
<li class="menu-option" @click="ItemDetail" v-if="item">{{ $t('context_menu.detail') }}</li>
<li class="menu-option" @click="downloadItem" v-if="isFile || isImage">{{ $t('context_menu.download') }}</li>
<li class="menu-option" @click="downloadItem" v-if="! isFolder && item">{{ $t('context_menu.download') }}</li>
</ul>
</div>
</template>
@@ -34,11 +34,14 @@
name: 'ContextMenu',
computed: {
...mapGetters(['app']),
isFolder() {
return this.item && this.item.type === 'folder'
},
isFile() {
return this.item && this.item.type === 'file' ? true : false
return (this.item && this.item.type !== 'folder') && (this.item && this.item.type !== 'image')
},
isImage() {
return this.item && this.item.type === 'image' ? true : false
return this.item && this.item.type === 'image'
},
isInFavourites() {
return this.app.favourites.find(el => el.unique_id == this.item.unique_id)

View File

@@ -1,31 +1,18 @@
<template>
<div v-if="fileInfoDetail">
<div class="file-headline" spellcheck="false">
<!--Image thumbnail-->
<div v-if="fileInfoDetail.type == 'image'" class="image-preview">
<img
@dblclick="$openImageOnNewTab(fileInfoDetail.file_url)"
:src="fileInfoDetail.thumbnail"
:alt="fileInfoDetail.name"
/>
</div>
<FilePreview />
<!--File info-->
<div class="flex">
<div class="icon">
<div class="icon-preview" @dblclick="getItemAction">
<FontAwesomeIcon
v-if="fileInfoDetail.type == 'folder'"
icon="folder"
></FontAwesomeIcon>
<FontAwesomeIcon
v-if="fileInfoDetail.type == 'file'"
icon="file"
></FontAwesomeIcon>
<FontAwesomeIcon
v-if="fileInfoDetail.type == 'image'"
icon="file-image"
></FontAwesomeIcon>
<FontAwesomeIcon v-if="fileInfoDetail.type == 'folder'" icon="folder"></FontAwesomeIcon>
<FontAwesomeIcon v-if="fileInfoDetail.type == 'file'" icon="file"></FontAwesomeIcon>
<FontAwesomeIcon v-if="fileInfoDetail.type == 'image'" icon="file-image"></FontAwesomeIcon>
<FontAwesomeIcon v-if="fileInfoDetail.type == 'video'" icon="file-video"></FontAwesomeIcon>
<FontAwesomeIcon v-if="fileInfoDetail.type == 'audio'" icon="file-audio"></FontAwesomeIcon>
</div>
</div>
<div class="file-info">
@@ -64,12 +51,16 @@
</template>
<script>
import FilePreview from '@/components/VueFileManagerComponents/FilesView/FilePreview'
import {mapGetters} from 'vuex'
import {debounce} from 'lodash'
import {events} from "@/bus"
export default {
name: 'FilesInfoPanel',
name: 'FileInfoPanel',
components: {
FilePreview
},
computed: {
...mapGetters(['fileInfoDetail'])
},
@@ -122,20 +113,6 @@
margin-bottom: 20px;
border-radius: 8px;
.image-preview {
width: 100%;
display: block;
margin-bottom: 7px;
img {
border-radius: 4px;
overflow: hidden;
width: 100%;
object-fit: cover;
cursor: pointer;
}
}
.flex {
display: flex;
align-items: top;
@@ -251,6 +228,13 @@
span {
color: $dark_mode_text_primary
}
.action-button {
.icon {
color: $dark_mode_text_primary;
}
}
}
}
}

View File

@@ -19,7 +19,7 @@
:class="{ 'is-clicked': isClicked, 'is-dragenter': area }"
>
<!--Thumbnail for item-->
<div class="icon-item" :class="data.type">
<div class="icon-item">
<!--If is file or image, then link item-->
<span v-if="isFile" class="file-icon-text">{{
data.mimetype
@@ -29,15 +29,10 @@
<FontAwesomeIcon v-if="isFile" class="file-icon" icon="file"/>
<!--Image thumbnail-->
<img v-if="isImage" :src="data.thumbnail" :alt="data.name"/>
<img v-if="isImage" class="image" :src="data.thumbnail" :alt="data.name"/>
<!--Else show only folder icon-->
<FontAwesomeIcon
v-if="isFolder"
:class="{'is-deleted': isDeleted}"
class="folder-icon"
icon="folder"
/>
<FontAwesomeIcon v-if="isFolder" :class="{'is-deleted': isDeleted}" class="folder-icon" icon="folder"/>
</div>
<!--Name-->
@@ -52,7 +47,7 @@
>
<!--Other attributes-->
<span v-if="isFile || isImage" class="item-size">{{
<span v-if="! isFolder" class="item-size">{{
data.filesize
}}</span>
@@ -82,7 +77,7 @@
return this.data.type === 'folder'
},
isFile() {
return this.data.type === 'file'
return this.data.type !== 'folder' && this.data.type !== 'image'
},
isImage() {
return this.data.type === 'image'
@@ -207,7 +202,6 @@
}
}
.file-wrapper {
position: relative;
text-align: center;
@@ -232,6 +226,11 @@
.name {
display: block;
&[contenteditable] {
-webkit-user-select: text;
user-select: text;
}
&[contenteditable='true']:hover {
text-decoration: underline;
}
@@ -282,6 +281,7 @@
}
.icon-item {
text-align: center;
position: relative;
height: 110px;
margin-bottom: 20px;
@@ -303,41 +303,33 @@
}
}
&.file {
.file-icon-text {
margin: 5px auto 0;
position: absolute;
text-align: center;
left: 0;
right: 0;
color: $theme;
font-weight: 600;
user-select: none;
max-width: 65px;
max-height: 20px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.file-icon-text {
margin: 5px auto 0;
position: absolute;
text-align: center;
left: 0;
right: 0;
color: $theme;
font-weight: 600;
user-select: none;
max-width: 65px;
max-height: 20px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
&.image {
img {
max-width: 95%;
object-fit: cover;
user-select: none;
height: 110px;
border-radius: 5px;
margin: 0 auto;
}
}
&.folder {
align-items: flex-end;
.image {
max-width: 95%;
object-fit: cover;
user-select: none;
height: 110px;
border-radius: 5px;
margin: 0 auto;
}
.folder-icon {
align-items: flex-end;
@include font-size(80);
margin: 0 auto;

View File

@@ -18,7 +18,7 @@
:class="{ 'is-clicked': isClicked, 'is-dragenter': area }"
>
<!--Thumbnail for item-->
<div class="icon-item" :class="data.type">
<div class="icon-item">
<!--If is file or image, then link item-->
<span v-if="isFile" class="file-icon-text">{{
data.mimetype | limitCharacters
@@ -28,15 +28,10 @@
<FontAwesomeIcon v-if="isFile" class="file-icon" icon="file"/>
<!--Image thumbnail-->
<img v-if="isImage" :src="data.thumbnail" :alt="data.name"/>
<img v-if="isImage" class="image" :src="data.thumbnail" :alt="data.name"/>
<!--Else show only folder icon-->
<FontAwesomeIcon
v-if="isFolder"
:class="{'is-deleted': isDeleted}"
class="folder-icon"
icon="folder"
/>
<FontAwesomeIcon v-if="isFolder" :class="{'is-deleted': isDeleted}" class="folder-icon" icon="folder"/>
</div>
<!--Name-->
@@ -50,7 +45,7 @@
>{{ itemName }}</span>
<!--Other attributes-->
<span v-if="isFile || isImage" class="item-size">{{ data.filesize }}, {{ timeStamp }}</span>
<span v-if="! isFolder" class="item-size">{{ data.filesize }}, {{ timeStamp }}</span>
<span v-if="isFolder" class="item-length">
{{ folderItems == 0 ? $t('folder.empty') : $tc('folder.item_counts', folderItems) }}, {{ timeStamp }}
@@ -81,7 +76,7 @@
return this.data.type === 'folder'
},
isFile() {
return this.data.type === 'file'
return this.data.type !== 'folder' && this.data.type !== 'image'
},
isImage() {
return this.data.type === 'image'
@@ -248,7 +243,11 @@
.name {
white-space: nowrap;
//display: inline-block;
&[contenteditable] {
-webkit-user-select: text;
user-select: text;
}
&[contenteditable='true']:hover {
text-decoration: underline;
@@ -276,6 +275,7 @@
}
.icon-item {
text-align: center;
position: relative;
flex: 0 0 50px;
line-height: 0;
@@ -305,38 +305,32 @@
}
}
&.file {
.file-icon-text {
line-height: 1;
top: 40%;
@include font-size(11);
margin: 0 auto;
position: absolute;
text-align: center;
.file-icon-text {
line-height: 1;
top: 40%;
@include font-size(11);
margin: 0 auto;
position: absolute;
text-align: center;
left: 0;
right: 0;
color: $theme;
font-weight: 600;
user-select: none;
max-width: 50px;
max-height: 20px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
left: 0;
right: 0;
color: $theme;
font-weight: 600;
user-select: none;
max-width: 50px;
max-height: 20px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
&.image {
img {
object-fit: cover;
user-select: none;
max-width: 100%;
border-radius: 5px;
width: 50px;
height: 50px;
}
.image {
object-fit: cover;
user-select: none;
max-width: 100%;
border-radius: 5px;
width: 50px;
height: 50px;
}
}

View File

@@ -0,0 +1,60 @@
<template>
<div v-if="canBePreview" class="preview">
<img v-if="fileInfoDetail.type == 'image'" :src="fileInfoDetail.thumbnail" :alt="fileInfoDetail.name" />
<audio v-else-if="fileInfoDetail.type == 'audio'" :src="fileInfoDetail.file_url" controlsList="nodownload" controls></audio>
<video v-else-if="fileInfoDetail.type == 'video'" controlsList="nodownload" disablePictureInPicture playsinline controls>
<source :src="fileInfoDetail.file_url" type="video/mp4">
</video>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import { includes } from 'lodash'
export default {
name: 'FilePreview',
computed: {
...mapGetters(['fileInfoDetail']),
canBePreview() {
return this.fileInfoDetail && ! includes([
'folder', 'file'
], this.fileInfoDetail.type)
}
},
}
</script>
<style scoped lang="scss">
@import "@assets/app.scss";
.preview {
width: 100%;
display: block;
margin-bottom: 7px;
img {
border-radius: 4px;
overflow: hidden;
width: 100%;
object-fit: cover;
}
audio {
width: 100%;
&::-webkit-media-controls-panel {
background-color: $light_background;
}
&::-webkit-media-controls-play-button {
color: $theme;
}
}
video {
width: 100%;
height: auto;
border-radius: 3px;
}
}
</style>

View File

@@ -1,5 +1,10 @@
<template>
<div class="file-content" :class="{ 'is-offset': uploadingFilesCount }">
<div class="file-content" :class="{ 'is-offset': uploadingFilesCount, 'is-dragging': isDragging }"
@dragover.prevent
@drop.stop.prevent="dropUpload($event)"
@dragover="dragEnter"
@dragleave="dragLeave"
>
<div
class="files-container"
ref="fileContainer"
@@ -18,7 +23,10 @@
<MobileActions v-if="$isMinimalScale()" />
<!--Item previews list-->
<div v-if="isList" class="file-list-wrapper">
<div
v-if="isList"
class="file-list-wrapper"
>
<transition-group
name="file"
tag="section"
@@ -27,7 +35,7 @@
>
<FileItemList
@dragstart="dragStart(item)"
@drop="dragFinish(item)"
@drop.stop.native.prevent="dragFinish(item, $event)"
@contextmenu.native.prevent="contextMenu($event, item)"
:data="item"
v-for="item in data"
@@ -47,7 +55,7 @@
>
<FileItemGrid
@dragstart="dragStart(item)"
@drop="dragFinish(item)"
@drop.native.prevent="dragFinish(item, $event)"
@contextmenu.native.prevent="contextMenu($event, item)"
:data="item"
v-for="item in data"
@@ -133,10 +141,23 @@
},
data() {
return {
draggingId: undefined
draggingId: undefined,
isDragging: false,
}
},
methods: {
dropUpload(event) {
// Upload external file
this.$uploadExternalFiles(event, this.currentFolder.unique_id)
this.isDragging = false
},
dragEnter() {
this.isDragging = true
},
dragLeave() {
this.isDragging = false
},
dragStart(data) {
events.$emit('dragstart', data)
@@ -144,15 +165,26 @@
// Store dragged folder
this.draggingId = data
},
dragFinish(data) {
// Prevent to drop on file or image
if (data.type !== 'folder' || this.draggingId === data) return
dragFinish(data, event) {
// Move folder to new parent
this.moveTo(this.draggingId, data)
},
moveTo(from_item, to_item) {
this.$store.dispatch('moveItem', [from_item, to_item])
if (event.dataTransfer.items.length == 0) {
// Prevent to drop on file or image
if (data.type !== 'folder' || this.draggingId === data) return
// Move folder to new parent
this.$store.dispatch('moveItem', [this.draggingId, data])
} else {
// Get unique_id of current folder
const unique_id = data.type !== 'folder' ? this.currentFolder.unique_id : data.unique_id
// Upload external file
this.$uploadExternalFiles(event, unique_id)
}
this.isDragging = false
},
contextMenu(event, item) {
events.$emit('contextMenu:show', event, item)
@@ -225,6 +257,10 @@
.file-content {
display: flex;
flex-wrap: nowrap;
&.is-dragging {
@include transform(scale(0.99));
}
}
.files-container {

View File

@@ -11,7 +11,7 @@
<ul class="menu-options">
<li class="menu-option"
@click="addToFavourites"
v-if="! $isTrashLocation() && fileInfoDetail && fileInfoDetail.type === 'folder'"
v-if="! $isTrashLocation() && fileInfoDetail && isFolder"
>
{{ isInFavourites ? $t('context_menu.remove_from_favourites') : $t('context_menu.add_to_favourites') }}
</li>
@@ -39,7 +39,7 @@
<li
class="menu-option"
@click="downloadItem"
v-if="isFile || isImage"
v-if="! isFolder"
>
{{ $t('context_menu.download') }}
</li>
@@ -76,19 +76,13 @@
return this.app.favourites.find(el => el.unique_id == this.fileInfoDetail.unique_id)
},
isFile() {
return this.fileInfoDetail && this.fileInfoDetail.type === 'file'
? true
: false
return (this.fileInfoDetail && this.fileInfoDetail.type !== 'folder') && (this.fileInfoDetail && this.fileInfoDetail.type !== 'image')
},
isImage() {
return this.fileInfoDetail && this.fileInfoDetail.type === 'image'
? true
: false
},
isFolder() {
return this.fileInfoDetail && this.fileInfoDetail.type === 'folder'
? true
: false
}
},
data() {