Folder tree dynamic navigator

This commit is contained in:
Čarodej
2022-01-21 16:46:17 +01:00
parent 6cb2a1bb9a
commit e2cfdd5345
11 changed files with 68 additions and 1332 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,27 +1,31 @@
<template>
<transition name="folder">
<div class="folder-item-wrapper">
<div
@click="goToFolder"
class="folder-item text-theme dark-text-theme flex"
:class="{'is-selected': isSelected, 'is-dragenter': area, 'is-inactive': disabledFolder || disabled && draggedItem.length > 0 }"
:style="indent"
@dragover.prevent="dragEnter"
@dragleave="dragLeave"
@drop="dragFinish()"
>
<chevron-right-icon
@click.stop.prevent="showTree"
<div>
<div
@click="goToFolder"
class="flex items-center py-2 rounded-lg border-2 border-transparent border-dashed cursor-pointer"
:class="{'border-theme': area, 'pointer-events-none opacity-50': disabledFolder || disabled && draggedItem.length > 0 }"
:style="indent"
@dragover.prevent="dragEnter"
@dragleave="dragLeave"
@drop="dragFinish()"
>
<div @click.stop.prevent="showTree" class="p-2 -ml-2 -my-2 cursor-pointer">
<chevron-right-icon
size="17"
class="icon-arrow"
:class="{'is-opened': isVisible, 'is-visible': nodes.folders.length !== 0}"
class="vue-feather"
:class="{'transform rotate-90': isVisible, 'opacity-0': nodes.folders.length === 0}"
/>
<folder-icon size="17" class="icon text-theme dark-text-theme" />
<span class="label">{{ nodes.name }}</span>
</div>
<TreeMenuNavigator :disabled="disableChildren" :depth="depth + 1" v-if="isVisible" :nodes="item" v-for="item in nodes.folders" :key="item.id" />
</div>
</transition>
</div>
<folder-icon size="17" class="mr-2.5 vue-feather" :class="{'text-theme': isSelected}" />
<b
class="font-bold text-sm max-w-1 overflow-hidden overflow-ellipsis whitespace-nowrap"
:class="{'text-theme': isSelected}"
>
{{ nodes.name }}
</b>
</div>
<TreeMenuNavigator :disabled="disableChildren" :depth="depth + 1" v-if="isVisible" :nodes="item" v-for="item in nodes.folders" :key="item.id" />
</div>
</template>
<script>
@@ -44,13 +48,14 @@
},
computed: {
...mapGetters([
'clipboard'
'clipboard',
]),
isSelected() {
return this.$route.params.id === this.nodes.id
},
disabledFolder() {
let disableFolder = false
if (this.draggedItem.length > 0) {
this.draggedItem.forEach(item => {
@@ -75,9 +80,9 @@
},
indent() {
let offset = window.innerWidth <= 1024 ? 17 : 22;
let offset = window.innerWidth <= 1024 ? 14 : 18;
let value = this.depth === 0 ? offset : offset + (this.depth * 20);
let value = this.depth === 0 ? offset : offset + (this.depth * 18);
return {paddingLeft: value + 'px'}
},
@@ -92,11 +97,7 @@
},
methods: {
goToFolder() {
if (this.$router.currentRoute.name === 'Public') {
this.$router.push({name: 'Public', params: {id: this.nodes.id}})
} else {
this.$router.push({name: 'Files', params: {id: this.nodes.id}})
}
this.$goToFileView(this.nodes.id)
},
dragFinish() {
// Move no selected item
@@ -144,100 +145,3 @@
}
}
</script>
<style lang="scss" scoped>
@import '/resources/sass/vuefilemanager/_variables';
@import '/resources/sass/vuefilemanager/_mixins';
.is-inactive {
opacity: 0.5;
pointer-events: none;
}
.is-dragenter {
border-radius: 8px;
}
.folder-item {
padding: 8px 0;
@include transition(150ms);
cursor: pointer;
position: relative;
white-space: nowrap;
width: 100%;
border: 2px dashed transparent;
.icon {
line-height: 0;
width: 15px;
margin-right: 9px;
vertical-align: middle;
margin-top: -1px;
path, line, polyline, rect, circle {
@include transition(150ms);
}
}
.icon-arrow {
@include transition(300ms);
margin-right: 4px;
vertical-align: middle;
opacity: 0;
&.is-visible {
opacity: 1;
}
&.is-opened {
@include transform(rotate(90deg));
}
}
.label {
@include transition(150ms);
@include font-size(13);
font-weight: 700;
vertical-align: middle;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
color: $text;
max-width: 130px;
}
&:hover,
&.is-selected {
.icon {
path, line, polyline, rect, circle {
color: inherit !important;;
}
}
.label {
color: inherit !important;
}
}
}
@media only screen and (max-width: 1024px) {
.folder-item {
padding: 8px 0;
}
}
// Dark mode
.dark {
.folder-item {
.label {
color: $dark_mode_text_primary;
}
}
}
</style>

View File

@@ -1,100 +0,0 @@
<template>
<div class="upgrade-banner">
<div class="header-title">
<hard-drive-icon size="19" class="icon"></hard-drive-icon>
<span class="title">{{ storage.used }}% From {{ storage.capacity_formatted }}</span>
</div>
<div class="content">
<p v-if="storage.used > 95" class="reach-capacity">{{ $t('upgrade_banner.title') }}</p>
<p v-else class="reach-capacity">{{ $t('upgrade_banner.description') }}</p>
</div>
<div v-if="config.app_allowed_payments" class="footer">
<router-link :to="{name: 'UpgradePlan'}" class="button">
{{ $t('upgrade_banner.button') }}
</router-link>
</div>
</div>
</template>
<script>
import ButtonBase from '/resources/js/components/FilesView/ButtonBase'
import { HardDriveIcon } from 'vue-feather-icons'
import { mapGetters } from 'vuex'
export default {
name: 'UpgradeSidebarBanner',
components: {
HardDriveIcon,
ButtonBase,
},
computed: {
...mapGetters(['config']),
storage() {
return this.$store.getters.user.relationships.storage.data.attributes
}
}
}
</script>
<style lang="scss" scoped>
@import '/resources/sass/vuefilemanager/_variables';
@import '/resources/sass/vuefilemanager/_mixins';
.upgrade-banner {
background: rgba($danger, 0.1);
padding: 10px;
border-radius: 6px;
margin: 0 16px;
}
.header-title {
margin-bottom: 12px;
display: flex;
align-items: center;
.icon {
margin-right: 10px;
line, path {
stroke: $danger;
}
}
.title {
@include font-size(13);
font-weight: 800;
color: $danger;
}
}
.content {
margin-bottom: 12px;
p {
@include font-size(12);
color: $danger;
font-weight: 700;
}
}
.button {
background: $danger;
border-radius: 50px;
padding: 6px 0;
width: 100%;
color: white;
text-align: center;
@include font-size(12);
font-weight: 700;
display: block;
box-shadow: 0 4px 10px rgba($danger, 0.35);
}
@media only screen and (max-width: 1024px) {
}
.dark {
}
</style>

View File

@@ -60,7 +60,7 @@
<!--Transaction detail-->
<tr v-if="row.data.attributes.metadata && showedTransactionDetailById === row.data.id">
<td colspan="7" class="dark:bg-2x-dark-foreground bg-light-background bg-opacity-50 rounded-lg overflow-hidden py-2 px-4">
<td colspan="7" class="rounded-lg overflow-hidden py-2">
<div class="flex items-center justify-between py-2 border-b dark:border-opacity-5 border-light border-dashed" v-for="(usage, i) in row.data.attributes.metadata" :key="i">
<div class="w-2/4 leading-none">
<b class="text-sm font-bold leading-none">

View File

@@ -7,19 +7,19 @@
>
<div
v-if="isVisible"
class="absolute transform -translate-y-full -translate-x-1/2 -top-4 ml-1.5 bg-gray-800 rounded-lg shadow-lg py-2 px-3 z-10"
class="absolute transform -translate-y-full -translate-x-1/2 -top-4 ml-1.5 dark:bg-white bg-gray-800 rounded-lg shadow-lg py-2 px-3 z-10"
>
<b class="text-white text-xs whitespace-nowrap block mb-2">
<b class="dark:text-gray-800 text-white text-xs whitespace-nowrap block mb-2">
{{ bar.created_at }}
</b>
<div class="flex items-center pb-1">
<span class="w-3 h-3 block bg-theme mr-2 rounded"></span>
<b class="text-white text-xs whitespace-nowrap">
<b class="dark:text-gray-800 text-white text-xs whitespace-nowrap">
{{ bar.amount }}
</b>
</div>
<div class="w-5 overflow-hidden inline-block absolute -bottom-2.5 left-0 right-0 mx-auto">
<div class="h-3 w-3 bg-gray-800 -rotate-45 transform origin-top-left"></div>
<div class="h-3 w-3 dark:bg-white bg-gray-800 -rotate-45 transform origin-top-left"></div>
</div>
</div>
<span class="bg-theme w-full h-full block rounded-lg"></span>

View File

@@ -23,7 +23,6 @@
<script>
import {FolderIcon, HomeIcon, LinkIcon, Trash2Icon, UploadCloudIcon, UserCheckIcon, UsersIcon, XIcon} from "vue-feather-icons";
import UpgradeSidebarBanner from '/resources/js/components/Others/UpgradeSidebarBanner'
import TreeMenuNavigator from '/resources/js/components/Others/TreeMenuNavigator'
import ContentSidebar from '/resources/js/components/Sidebar/ContentSidebar'
import ContentGroup from '/resources/js/components/Sidebar/ContentGroup'
@@ -33,7 +32,6 @@
export default {
name: "NavigationSharePanel",
components: {
UpgradeSidebarBanner,
TreeMenuNavigator,
ContentSidebar,
ContentGroup,

View File

@@ -4,11 +4,6 @@
<chevrons-left-icon size="18"/>
</div>
<!--Empty storage warning-->
<!-- <ContentGroup v-if="user && config.storageLimit && storage.used > 95">
<UpgradeSidebarBanner/>
</ContentGroup>-->
<!--Locations-->
<ContentGroup :title="$t('sidebar.locations_title')">
<div class="menu-list-wrapper vertical">
@@ -74,7 +69,7 @@
<span v-if="tree.length === 0" class="empty-note navigator">
{{ $t('sidebar.folders_empty') }}
</span>
<TreeMenuNavigator class="folder-tree" :depth="0" :nodes="folder" v-for="folder in tree" :key="folder.id"/>
<TreeMenuNavigator v-if="navigation" class="folder-tree" :depth="0" :nodes="folder" v-for="folder in tree" :key="folder.id"/>
</ContentGroup>
<!--Favourites-->
@@ -101,7 +96,6 @@
<script>
import { ChevronsLeftIcon, FolderIcon, HomeIcon, LinkIcon, Trash2Icon, UploadCloudIcon, UserCheckIcon, UsersIcon, XIcon} from "vue-feather-icons";
import UpgradeSidebarBanner from '/resources/js/components/Others/UpgradeSidebarBanner'
import TreeMenuNavigator from '/resources/js/components/Others/TreeMenuNavigator'
import ContentSidebar from '/resources/js/components/Sidebar/ContentSidebar'
import ContentGroup from '/resources/js/components/Sidebar/ContentGroup'
@@ -111,7 +105,6 @@
export default {
name: "PanelNavigationFiles",
components: {
UpgradeSidebarBanner,
TreeMenuNavigator,
ContentSidebar,
ContentGroup,
@@ -128,6 +121,7 @@ export default {
computed: {
...mapGetters([
'isVisibleNavigationBars',
'navigation',
'clipboard',
'config',
'user',
@@ -139,7 +133,15 @@ export default {
return this.$store.getters.user.data.attributes.storage
},
tree() {
return this.user.data.attributes.folders
return {
'RecentUploads': this.navigation[0].folders,
'MySharedItems': this.navigation[0].folders,
'Trash': this.navigation[0].folders,
'Public': this.navigation[0].folders,
'Files': this.navigation[0].folders,
'TeamFolders': this.navigation[1].folders,
'SharedWithMe': this.navigation[2].folders,
}[this.$route.name]
},
},
data() {
@@ -197,6 +199,8 @@ export default {
created() {
// Listen for dragstart folder items
events.$on('dragstart', item => this.draggedItem = item)
this.$store.dispatch('getFolderTree')
}
}
</script>

View File

@@ -121,19 +121,6 @@ class User extends Authenticatable implements MustVerifyEmail
->sum('filesize');
}
/**
* Get user full folder tree
*/
public function getFolderTreeAttribute(): Collection
{
return Folder::with(['folders.shared', 'shared:token,id,item_id,permission,is_protected,expire_in'])
->where('parent_id')
->where('team_folder', false)
->where('user_id', $this->id)
->sortable()
->get();
}
public function settings(): HasOne
{
return $this->hasOne(UserSetting::class);

View File

@@ -36,7 +36,6 @@ class UserResource extends JsonResource
'role' => $this->role,
'two_factor_authentication' => $this->two_factor_secret ? true : false,
'socialite_account' => $this->password ? false : true,
'folders' => $this->folder_tree,
'storage' => $this->storage,
'created_at' => format_date($this->created_at, '%d. %b. %Y'),
'updated_at' => format_date($this->updated_at, '%d. %B. %Y'),

View File

@@ -134,13 +134,13 @@ class UserStorageResource extends JsonResource
->get();
$upload = $trafficRecords->map(fn($record) => [
'created_at' => format_date($record->created_at),
'created_at' => format_date($record->created_at, '%d. %B'),
'percentage' => $uploadMax !== 0 ? round(($record->upload / $uploadMax) * 100, 2) : 0,
'amount' => Metric::bytes($record->upload)->format(),
]);
$download = $trafficRecords->map(fn($record) => [
'created_at' => format_date($record->created_at),
'created_at' => format_date($record->created_at, '%d. %B'),
'percentage' => $downloadMax !== 0 ? round(($record->download / $downloadMax) * 100, 2) : 0,
'amount' => Metric::bytes($record->download)->format(),
]);

View File

@@ -161,7 +161,6 @@ class UserAccountTest extends TestCase
'role' => $user->role,
'socialite_account' => false,
'two_factor_authentication' => false,
'folders' => [],
'storage' => [
'used' => 0,
'used_formatted' => '0%',