Version 1.2

- Move your items by folder tree
- Fixed bug with image rotation on iOS Device
This commit is contained in:
MakingCG
2020-03-28 12:26:51 +01:00
parent 7cdf463e4e
commit ea74c8885f
17 changed files with 1037 additions and 53 deletions

View File

@@ -1,14 +1,12 @@
<template>
<div @click="fileViewClick" @contextmenu.prevent.capture="contextMenu($event, undefined)" id="files-view" :class="filesViewWidth">
<ContextMenu/>
<MobileOptionList/>
<DesktopToolbar v-if="! $isMinimalScale()"/>
<FilesContainer/>
</div>
</template>
<script>
import MobileOptionList from '@/components/VueFileManagerComponents/FilesView/MobileOptionList'
import UploadProgress from '@/components/VueFileManagerComponents/FilesView/UploadProgress'
import FilesContainer from '@/components/VueFileManagerComponents/FilesView/FilesContainer'
import DesktopToolbar from '@/components/VueFileManagerComponents/FilesView/DesktopToolbar'
@@ -20,7 +18,6 @@
export default {
name: 'FilesView',
components: {
MobileOptionList,
UploadProgress,
FilesContainer,
DesktopToolbar,

View File

@@ -117,7 +117,7 @@
right: 0;
left: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.5);
background: rgba(17, 20, 29, 0.5);
}
}

View File

@@ -6,11 +6,20 @@
v-show="isVisible"
>
<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 ? 'Remove Favourite' : 'Add To Favourites' }}</li>
<li class="menu-option" @click="$store.dispatch('restoreItem', item)" v-if="item && $isTrashLocation()">Restore</li>
<li class="menu-option" @click="createFolder" v-if="! $isTrashLocation()">Create Folder</li>
<!--Edits-->
<li class="menu-option" @click="removeItem" v-if="! $isTrashLocation() && item">Delete</li>
<li class="menu-option" @click="moveItem" v-if="! $isTrashLocation() && item">Move</li>
<!--Trash-->
<li class="menu-option" @click="$store.dispatch('restoreItem', item)" v-if="item && $isTrashLocation()">Restore</li>
<li class="menu-option" @click="$store.dispatch('emptyTrash')" v-if="$isTrashLocation()">Empty Trash</li>
<!--Others-->
<li class="menu-option" @click="ItemDetail" v-if="item">Detail</li>
<li class="menu-option" @click="downloadItem" v-if="isFile || isImage">Download</li>
</ul>
@@ -44,6 +53,10 @@
}
},
methods: {
moveItem() {
// Move item fire popup
events.$emit('popup:move-item', this.item);
},
addToFavourites() {
if (this.app.favourites && ! this.app.favourites.find(el => el.unique_id == this.item.unique_id)) {
this.$store.dispatch('addToFavourites', this.item)

View File

@@ -29,6 +29,13 @@
>
Rename
</li>
<li
class="menu-option"
@click="moveItem"
v-if="fileInfoDetail"
>
Move
</li>
<li
class="menu-option"
@click="downloadItem"
@@ -90,6 +97,10 @@
}
},
methods: {
moveItem() {
// Move item fire popup
events.$emit('popup:move-item', this.fileInfoDetail);
},
addToFavourites() {
if (this.app.favourites && ! this.app.favourites.find(el => el.unique_id == this.fileInfoDetail.unique_id)) {
this.$store.dispatch('addToFavourites', this.fileInfoDetail)

View File

@@ -1,45 +1,45 @@
<template>
<div id="loading-bar-spinner" class="spinner">
<div class="spinner-icon"></div>
</div>
<div id="loading-bar-spinner" class="spinner">
<div class="spinner-icon"></div>
</div>
</template>
<script>
export default {
name: 'Spinner'
}
export default {
name: 'Spinner'
}
</script>
<style scoped lang="scss">
@import "@assets/app.scss";
@import "@assets/app.scss";
#loading-bar-spinner.spinner {
left: 50%;
margin-left: -20px;
top: 50%;
margin-top: -20px;
position: absolute;
z-index: 19 !important;
animation: loading-bar-spinner 400ms linear infinite;
}
#loading-bar-spinner.spinner {
left: 50%;
margin-left: -20px;
top: 50%;
margin-top: -20px;
position: absolute;
z-index: 19 !important;
animation: loading-bar-spinner 400ms linear infinite;
}
#loading-bar-spinner.spinner .spinner-icon {
width: 40px;
height: 40px;
border: solid 4px transparent;
border-top-color: $theme !important;
border-left-color: $theme !important;
border-radius: 50%;
}
#loading-bar-spinner.spinner .spinner-icon {
width: 40px;
height: 40px;
border: solid 4px transparent;
border-top-color: $theme !important;
border-left-color: $theme !important;
border-radius: 50%;
}
@keyframes loading-bar-spinner {
0% {
transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes loading-bar-spinner {
0% {
transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
transform: rotate(360deg);
}
}
</style>

View File

@@ -0,0 +1,314 @@
<template>
<transition name="vignette">
<div class="popup" v-if="isVisibleWrapper">
<transition name="popup">
<div v-if="isVisiblePopup" class="popup-wrapper">
<!--Title-->
<div class="popup-header">
<h1 class="title">Move Item</h1>
<!--<p v-if="message" class="message">{{ message }}</p>-->
</div>
<!--Content-->
<div class="popup-content" v-if="app && pickedItem">
<Spinner v-if="isLoadingTree" />
<div v-if="! isLoadingTree">
<ThumbnailItem class="item-thumbnail" :file="pickedItem" />
<TreeMenu :depth="1" :nodes="items" v-for="items in app.folders" :key="items.unique_id" />
</div>
</div>
<!--Actions-->
<div class="actions">
<ButtonBase
class="popup-button"
@click.native="closePopup"
button-style="secondary"
>Cancel
</ButtonBase>
<ButtonBase
class="popup-button"
@click.native="moveItem"
:button-style="selectedFolder ? 'theme' : 'secondary'"
>Move
</ButtonBase>
</div>
</div>
</transition>
<div class="popup-vignette" @click="closePopup"></div>
</div>
</transition>
</template>
<script>
import ThumbnailItem from '@/components/VueFileManagerComponents/Others/ThumbnailItem'
import ButtonBase from '@/components/VueFileManagerComponents/FilesView/ButtonBase'
import Spinner from '@/components/VueFileManagerComponents/FilesView/Spinner'
import TreeMenu from '@/components/VueFileManagerComponents/Others/TreeMenu'
import {mapGetters} from 'vuex'
import {events} from '@/bus'
export default {
name: 'PopupMoveItem',
components: {
ThumbnailItem,
ButtonBase,
TreeMenu,
Spinner,
},
computed: {
...mapGetters(['app']),
},
data() {
return {
isVisibleWrapper: false,
isVisiblePopup: false,
selectedFolder: undefined,
pickedItem: undefined,
isLoadingTree: true,
}
},
methods: {
moveItem() {
if (! this.selectedFolder) return
// Move item
this.$store.dispatch('moveItem', [this.pickedItem, this.selectedFolder])
// Close popup
this.closePopup()
},
closePopup() {
// Hide popup wrapper
this.isVisibleWrapper = false
this.isVisiblePopup = false
// Clear selected folder
this.selectedFolder = undefined
}
},
mounted() {
events.$on('pick-folder', unique_id => {
this.selectedFolder = unique_id
})
// Show popup
events.$on('popup:move-item', item => {
this.isLoadingTree = true
// Get folder tree
this.$store.dispatch('getFolderTree').then(() => {
this.isLoadingTree = false
}).catch(() => {
this.isLoadingTree = false
})
// Make popup visible
this.isVisibleWrapper = true
this.isVisiblePopup = true
// Store picked item
this.pickedItem = item
})
// Close popup
events.$on('popup:close', () => this.closePopup())
}
}
</script>
<style scoped lang="scss">
@import "@assets/app.scss";
.popup {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 999;
overflow: auto;
display: grid;
padding: 40px;
.popup-vignette {
position: fixed;
top: 0;
right: 0;
left: 0;
bottom: 0;
background: rgba(17, 20, 29, 0.5);
}
}
.popup-wrapper {
box-shadow: 0 7px 250px rgba(25, 54, 60, 0.2);
border-radius: 8px;
background: white;
margin: auto;
width: 480px;
z-index: 12;
}
.popup-header {
padding: 20px;
.title {
@include font-size(20);
font-weight: 900;
color: $text;
}
.message {
@include font-size(16);
color: #8b8f9a;
margin-top: 5px;
}
}
.popup-content {
height: 400px;
overflow-y: auto;
}
.item-thumbnail {
margin-bottom: 20px;
}
.actions {
padding: 20px;
margin: 0 -10px;
display: flex;
.popup-button {
width: 100%;
margin: 0 10px;
}
}
// Desktop, tablet
.medium, .large {
// Animations
.popup-enter-active {
animation: popup-in 0.35s 0.15s ease both;
}
.popup-leave-active {
animation: popup-in 0.15s ease reverse;
}
}
// Mobile styles
.small {
.popup-wrapper {
position: fixed;
top: 0;
bottom: 0;
right: 0;
left: 0;
transform: translateY(0) scale(1);
box-shadow: none;
width: 100%;
border-radius: 0px;
.popup-content {
top: 57px;
bottom: 72px;
position: absolute;
left: 0;
right: 0;
height: initial;
}
.actions {
position: absolute;
bottom: 0;
left: 0;
right: 0;
}
}
.popup-header {
padding: 15px;
}
.actions {
padding: 15px;
}
// Animations
.popup-enter-active {
animation: popup-slide-in 0.35s 0.15s ease both;
}
.popup-leave-active {
animation: popup-slide-in 0.15s ease reverse;
}
}
@keyframes popup-in {
0% {
opacity: 0;
transform: scale(0.7);
}
100% {
opacity: 1;
transform: scale(1);
}
}
@keyframes popup-slide-in {
0% {
transform: translateY(100%);
}
100% {
transform: translateY(0);
}
}
// Dark mode
@media (prefers-color-scheme: dark) {
.popup .popup-vignette {
background: $dark_mode_vignette;
}
.popup-wrapper {
background: $dark_mode_foreground;
}
.popup-header {
.title {
color: $dark_mode_text_primary;
}
.message {
color: $dark_mode_text_secondary;
}
}
}
.vignette-enter-active {
animation: vignette-in 0.15s ease;
}
.vignette-leave-active {
animation: vignette-in 0.15s 0.15s ease reverse;
}
@keyframes vignette-in {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
</style>

View File

@@ -0,0 +1,187 @@
<template>
<div class="file-item">
<!--Thumbnail for item-->
<div class="icon-item" :class="file.type">
<!--If is file or image, then link item-->
<span v-if="isFile" class="file-icon-text">{{ file.mimetype }}</span>
<!--Folder thumbnail-->
<FontAwesomeIcon v-if="isFile" class="file-icon" icon="file"/>
<!--Image thumbnail-->
<img v-if="isImage" :src="file.thumbnail" :alt="file.name"/>
<!--Else show only folder icon-->
<FontAwesomeIcon v-if="isFolder" class="folder-icon" icon="folder"/>
</div>
<!--Name-->
<div class="item-name">
<!--Name-->
<span class="name">{{ file.name }}</span>
<!--Other attributes-->
<span class="subtitle">Original Location: {{ currentFolder.name }}</span>
</div>
</div>
</template>
<script>
import {mapGetters} from 'vuex'
export default {
name: 'ThumbnailItem',
props: ['file'],
computed: {
...mapGetters(['currentFolder']),
isFolder() {
return this.file.type === 'folder'
},
isFile() {
return this.file.type === 'file'
},
isImage() {
return this.file.type === 'image'
}
},
filters: {
pluralize(word, amount) {
return amount > 1 ? word + 's' : word
}
},
}
</script>
<style scoped lang="scss">
@import "@assets/app.scss";
.file-item {
display: flex;
align-items: center;
padding: 0 20px;
.item-name {
display: block;
margin-left: 10px;
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
.subtitle {
@include font-size(11);
font-weight: 100;
color: $text-muted;
display: block;
}
.name {
white-space: nowrap;
color: $text;
@include font-size(14);
font-weight: 700;
max-height: 40px;
overflow: hidden;
text-overflow: ellipsis;
}
}
.icon-item {
position: relative;
min-width: 40px;
.file-icon {
@include font-size(35);
path {
fill: #fafafc;
stroke: #dfe0e8;
stroke-width: 1;
}
}
.folder-icon {
@include font-size(36);
path {
fill: $theme;
}
}
&.file {
text-align: center;
.file-icon-text {
top: 40%;
@include font-size(9);
margin: 0 auto;
position: absolute;
text-align: center;
left: 0;
right: 0;
color: $theme;
font-weight: 600;
user-select: none;
max-width: 20px;
max-height: 20px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
&.image {
line-height: 0;
img {
object-fit: cover;
user-select: none;
max-width: 100%;
border-radius: 5px;
width: 36px;
height: 36px;
}
}
}
}
.small {
.file-item {
padding: 0 15px;
margin-bottom: 10px;
}
}
@media (prefers-color-scheme: dark) {
.file-item {
.icon-item .file-icon {
path {
fill: $dark_mode_background;
stroke: #2F3C54;
}
}
.item-name .name {
color: $dark_mode_text_primary;
}
}
}
@media (max-width: 690px) and (prefers-color-scheme: dark) {
.file-item {
.icon-item .file-icon {
path {
fill: $dark_mode_foreground;
}
}
}
}
</style>

View File

@@ -0,0 +1,160 @@
<template>
<!--Folder Icon-->
<div class="folder-item-wrapper">
<div class="folder-item" :class="{'is-selected': isSelected}" @click="showTree" :style="indent">
<FontAwesomeIcon class="icon-chevron" :class="{'is-opened': isVisible, 'is-visible': nodes.folders.length !== 0}" icon="chevron-right"/>
<FontAwesomeIcon class="icon" :icon="directoryIcon"/>
<span class="label">{{ nodes.name }}</span>
</div>
<TreeMenu :depth="depth + 1" v-if="isVisible" :nodes="item" v-for="item in nodes.folders" :key="item.unique_id" />
</div>
</template>
<script>
import TreeMenu from '@/components/VueFileManagerComponents/Others/TreeMenu'
import {events} from "@/bus"
export default {
name: 'TreeMenu',
props: [
'nodes', 'depth'
],
components: {
TreeMenu,
},
computed: {
indent() {
return { paddingLeft: this.depth * 25 + 'px' }
},
directoryIcon() {
if (this.nodes.location === 'base') {
return 'hdd'
} else {
return 'folder'
}
}
},
data() {
return {
isVisible: false,
isSelected: false,
}
},
methods: {
showTree() {
this.isVisible = ! this.isVisible
events.$emit('pick-folder', this.nodes)
}
},
created() {
// Show first location
if (this.depth == 1) this.isVisible = true
// Select clicked folder
events.$on('pick-folder', node => {
this.isSelected = false
if (this.nodes.unique_id == node.unique_id) this.isSelected = true
})
}
}
</script>
<style lang="scss" scoped>
@import "@assets/app.scss";
.folder-item {
display: block;
padding: 10px 20px;
@include transition(150ms);
cursor: pointer;
position: relative;
white-space: nowrap;
.icon {
@include font-size(18);
margin-right: 9px;
vertical-align: middle;
path {
fill: $text;
}
}
.icon-chevron {
@include transition(300ms);
@include font-size(13);
margin-right: 9px;
vertical-align: middle;
opacity: 0;
&.is-visible {
opacity: 1;
}
&.is-opened {
@include transform(rotate(90deg));
}
path {
fill: rgba($text, 0.25);
}
}
.label {
@include font-size(15);
font-weight: 700;
vertical-align: middle;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
}
&:hover {
background: $light_background;
}
&.is-selected {
background: rgba($theme, .1);
.icon {
path {
fill: $theme;
}
}
.label {
color: $theme;
}
}
}
// Dark mode
@media (prefers-color-scheme: dark) {
.folder-item {
&:hover {
background: $dark_mode_background;
}
.icon-chevron {
path {
fill: $theme;
}
}
}
&.is-selected {
background: rgba($theme, .1);
}
}
</style>