File preview refactoring

This commit is contained in:
Čarodej
2022-02-03 10:49:14 +01:00
parent b751429dc5
commit bb9280d050
8 changed files with 158 additions and 497 deletions

View File

@@ -1,5 +1,13 @@
<template>
<div v-if="isFullPreview" class="file-preview z-40" ref="filePreview" tabindex="-1" @keydown.esc="closeFilePreview" @keydown.right="next" @keydown.left="prev">
<div
v-if="isFullPreview"
class="fixed z-40 h-full w-full bg-white dark:bg-dark-background"
ref="filePreview"
tabindex="-1"
@keydown.esc="closeFilePreview"
@keydown.right="next"
@keydown.left="prev"
>
<FilePreviewToolbar />
<FilePreviewMedia />
</div>
@@ -44,20 +52,3 @@ export default {
},
}
</script>
<style lang="scss" scoped>
@import '../../../sass/vuefilemanager/variables';
.file-preview {
width: 100%;
height: 100%;
position: fixed;
background-color: white;
}
.dark {
.file-preview {
background-color: $dark_mode_background;
}
}
</style>

View File

@@ -1,28 +1,39 @@
<template>
<div v-if="currentFile" class="file-preview-wrapper">
<!--Arrow navigation-->
<div v-if="files.length > 1" class="navigation-arrows">
<div @click.prevent="prev" class="prev">
<chevron-left-icon size="17" />
<div v-if="currentFile" class="absolute lg:top-[66px] top-[56px] left-0 right-0 bottom-0 select-none">
<!--Arrow navigation-->
<div v-if="!$isMobile() && files.length > 1" class="">
<div @click.prevent="prev" class="fixed top-1/2 left-0 p-3 cursor-pointer">
<chevron-left-icon size="20" />
</div>
<div @click.prevent="next" class="next">
<chevron-right-icon size="17" />
<div @click.prevent="next" class="fixed top-1/2 right-0 p-3 cursor-pointer">
<chevron-right-icon size="20" />
</div>
</div>
<!--File preview-->
<div class="file-wrapper-preview">
<!--Show PDF-->
<!--Desktop preview-->
<div v-if="!$isMobile() && (isAudio || isImage || isVideo)" class="w-full h-full flex justify-center items-center">
<!--Show PDF-->
<PdfFile v-if="isPDF" :file="currentFile" />
<!--Show Audio, Video and Image-->
<div v-if="isAudio || isImage || isVideo" class="file-wrapper">
<Audio v-if="isAudio" :file="currentFile" />
<div class="w-full h-full flex items-center justify-center">
<Audio v-if="isAudio" :file="currentFile"/>
<Video v-if="isVideo" :file="currentFile" />
<ImageFile v-if="isImage" :file="currentFile" />
<ImageFile v-if="isImage" :file="currentFile" class="max-w-[100%] max-h-[100%] self-center mx-auto" />
</div>
</div>
<!--Mobile Preview-->
<div v-if="$isMobile() && (isAudio || isImage || isVideo)" @scroll="checkGroupInView" id="group-box" class="flex gap-6 snap-x snap-mandatory overflow-x-auto h-full">
<div v-for="(file, i) in files" :key="i" :id="`group-${file.data.id}`" class="w-screen h-full flex items-center justify-center snap-center shrink-0">
<ImageFile v-if="isImage" :file="file" class="max-w-[100%] max-h-[100%] self-center mx-auto"/>
<Audio v-if="isAudio" :file="file"/>
<Video v-if="isVideo" :file="file" />
</div>
</div>
</div>
</template>
@@ -99,6 +110,25 @@ export default {
},
},
methods: {
checkGroupInView: _.debounce(function () {
this.files.forEach((file, index) => {
let element = document.getElementById(`group-${file.data.id}`).getBoundingClientRect()
let scrollBox = document.getElementById('group-box').getBoundingClientRect()
// Get video
const video = document.querySelector(`#group-${file.data.id} video`);
// Pause video when playing
if (video && !video.paused) {
video.pause()
}
// Check if the group is in the viewport of group-box
if (element.left === scrollBox.left) {
this.currentIndex = index
}
})
}, 100),
getFilesForView() {
let requestedFile = this.clipboard[0]
@@ -143,104 +173,3 @@ export default {
},
}
</script>
<style lang="scss">
@import '../../../sass/vuefilemanager/variables';
@import '../../../sass/vuefilemanager/mixins';
.navigation-arrows {
.prev,
.next {
cursor: pointer;
position: absolute;
top: 45%;
display: flex;
justify-content: center;
color: $text;
border-radius: 50%;
text-decoration: none;
user-select: none;
filter: drop-shadow(0px 1px 0 rgba(255, 255, 255, 1));
padding: 10px;
z-index: 2;
}
.next {
right: 0;
}
.prev {
left: 0;
}
}
.file-preview-wrapper {
height: calc(100% - 72px);
top: 72px;
position: relative;
background-color: white;
}
.file-wrapper-preview {
width: 100%;
height: 100%;
padding: 30px 0px;
display: flex;
overflow: hidden;
justify-content: center;
align-items: center;
background-color: white;
.file-wrapper {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
.file-shadow {
box-shadow: 0 8px 40px rgba(17, 26, 52, 0.05);
}
.file {
max-width: 100%;
max-height: 100%;
align-self: center;
}
.audio {
border-radius: 28px;
}
img {
border-radius: 4px;
}
}
}
@media only screen and (max-width: 960px) {
.file-preview-wrapper {
top: 53px;
}
}
.dark {
.navigation-arrows {
.prev,
.next {
color: $light-text;
filter: drop-shadow(0px 1px 0 rgba(17, 19, 20, 1));
}
}
.file-wrapper-preview {
background-color: $dark_mode_background;
.file-wrapper {
.file-shadow {
box-shadow: 0 8px 40px rgba(0, 0, 0, 0.1);
}
}
}
}
</style>

View File

@@ -1,66 +1,97 @@
<template>
<div class="navigation-panel" v-if="currentFile">
<div class="name-wrapper">
<div v-if="currentFile" class="items-center px-3.5 py-4 lg:grid lg:grid-cols-3 lg:py-3">
<div class="flex items-center justify-between lg:w-auto lg:justify-start">
<!--Close icon-->
<span @click="closeFullPreview" class="-m-3 p-3">
<x-icon size="17" class="icon-close hover-text-theme" />
</span>
<!--Item name-->
<div class="name-count-wrapper">
<p class="title">{{ currentFile.data.attributes.name }}</p>
<span v-if="!fastPreview" class="file-count"> ({{ showingImageIndex + ' ' + $t('pronouns.of') + ' ' + files.length }}) </span>
<div @click="closeFullPreview" class="order-last -m-3 cursor-pointer p-3 lg:order-none">
<x-icon size="16" class="vue-feather" />
</div>
<!--Context menu handler-->
<PopoverWrapper>
<!--Icon-->
<span @click.stop="showItemContextMenu" class="-m-3 p-3">
<div class="inline-block rounded-md bg-light-background py-0.5 px-1.5 align-middle transition-all duration-200 dark:bg-dark-foreground lg:bg-transparent">
<more-horizontal-icon size="14" />
</div>
</span>
<!--Item name-->
<div class="flex items-center">
<div class="mr-3 ml-0 flex items-center lg:mx-3">
<span
class="inline-block max-w-[230px] overflow-hidden text-ellipsis whitespace-nowrap text-sm font-bold"
>
{{ currentFile.data.attributes.name }}
</span>
<!--Desktop context menu-->
<PopoverItem name="file-preview-contextmenu" side="right">
<OptionGroup>
<Option @click.native="$renameFileOrFolder(currentFile)" :title="$t('context_menu.rename')" icon="rename" />
<Option @click.native="$moveFileOrFolder(currentFile)" :title="$t('context_menu.move')" icon="move-item" />
<Option @click.native="$shareFileOrFolder(currentFile)" :title="sharingTitle" icon="share" v-if="$checkPermission('master')" />
<Option @click.native="$deleteFileOrFolder(currentFile)" :title="$t('context_menu.delete')" icon="trash" class="menu-option" />
</OptionGroup>
<OptionGroup>
<Option @click.native="downloadItem" :title="$t('context_menu.download')" icon="download" />
</OptionGroup>
</PopoverItem>
</PopoverWrapper>
<span v-if="!fastPreview" class="ml-1 text-sm font-bold">
({{ showingImageIndex + ' ' + $t('pronouns.of') + ' ' + files.length }})
</span>
</div>
<!--Context menu handler-->
<PopoverWrapper>
<!--Icon-->
<span @click.stop="showItemContextMenu" class="-m-3 p-3">
<div
class="inline-block rounded-md bg-light-background py-0.5 px-1.5 align-middle transition-all duration-200 dark:bg-dark-foreground lg:bg-transparent"
>
<more-horizontal-icon size="14" />
</div>
</span>
<!--Desktop context menu-->
<PopoverItem name="file-preview-contextmenu" side="right">
<OptionGroup>
<Option
@click.native="$renameFileOrFolder(currentFile)"
:title="$t('context_menu.rename')"
icon="rename"
/>
<Option
@click.native="$moveFileOrFolder(currentFile)"
:title="$t('context_menu.move')"
icon="move-item"
/>
<Option
@click.native="$shareFileOrFolder(currentFile)"
:title="sharingTitle"
icon="share"
v-if="$checkPermission('master')"
/>
<Option
@click.native="$deleteFileOrFolder(currentFile)"
:title="$t('context_menu.delete')"
icon="trash"
class="menu-option"
/>
</OptionGroup>
<OptionGroup>
<Option @click.native="downloadItem" :title="$t('context_menu.download')" icon="download" />
</OptionGroup>
</PopoverItem>
</PopoverWrapper>
</div>
</div>
<!--Item metadata-->
<div class="created-at-wrapper">
<p>
{{ currentFile.data.attributes.filesize }},
{{ currentFile.data.attributes.created_at }}
</p>
</div>
<!--Item info-->
<small class="hidden text-center text-tiny font-normal text-gray-600 lg:block">
{{ currentFile.data.attributes.filesize }}, {{ currentFile.data.attributes.created_at }}
</small>
<!--Icon actions-->
<div class="navigation-icons">
<div v-if="isPdf" class="navigation-tool-wrapper">
<!--Actions-->
<div class="hidden items-center lg:flex lg:justify-end">
<div v-if="isPdf">
<ToolbarButton @click.native="decreaseSizeOfPDF" source="zoom-out" :action="$t('pdf_zoom_out')" />
<ToolbarButton @click.native="increaseSizeOfPDF" source="zoom-in" :action="$t('pdf_zoom_in')" />
</div>
<div class="navigation-tool-wrapper">
<ToolbarButton @click.native="downloadItem" class="mobile-hide" source="download" :action="$t('actions.download')" />
<div class="ml-5">
<ToolbarButton @click.native="downloadItem" source="download" :action="$t('actions.download')" />
<ToolbarButton
v-if="canShareItem"
@click.native="$shareFileOrFolder(currentFile)"
class="mobile-hide"
:class="{ 'is-inactive': !canShareItem }"
source="share"
:action="$t('actions.share')"
/>
<ToolbarButton v-if="isImage" @click.native="printMethod()" source="print" :action="$t('actions.print')" />
<ToolbarButton
v-if="isImage"
@click.native="printMethod()"
source="print"
:action="$t('actions.print')"
/>
</div>
</div>
</div>
@@ -94,7 +125,9 @@ export default {
return this.fastPreview ? this.fastPreview : this.clipboard[0]
},
sharingTitle() {
return this.currentFile.data.relationships.shared ? this.$t('context_menu.share_edit') : this.$t('context_menu.share')
return this.currentFile.data.relationships.shared
? this.$t('context_menu.share_edit')
: this.$t('context_menu.share')
},
isImage() {
return this.currentFile.data.type === 'image'
@@ -154,7 +187,10 @@ export default {
win.print()
},
downloadItem() {
this.$downloadFile(this.currentFile.data.attributes.file_url, this.currentFile.data.attributes.name + '.' + this.currentFile.data.attributes.mimetype)
this.$downloadFile(
this.currentFile.data.attributes.file_url,
this.currentFile.data.attributes.name + '.' + this.currentFile.data.attributes.mimetype
)
},
closeFullPreview() {
events.$emit('file-preview:hide')
@@ -162,188 +198,3 @@ export default {
},
}
</script>
<style lang="scss" scoped>
@import '../../../sass/vuefilemanager/variables';
@import '../../../sass/vuefilemanager/mixins';
.name-wrapper {
width: 33%;
height: 22px;
display: flex;
position: relative;
align-items: center;
flex-grow: 1;
align-self: center;
white-space: nowrap;
.name-count-wrapper {
margin-left: 6px;
margin-right: 6px;
.file-count {
@include font-size(15);
line-height: 1;
font-weight: 700;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
vertical-align: middle;
align-self: center;
color: $text;
}
.title {
@include font-size(15);
max-width: 250px;
line-height: 1;
font-weight: 700;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
vertical-align: middle;
color: $text;
}
@media (max-width: 570px) {
.title {
max-width: 180px;
@include font-size(17);
}
.file-count {
@include font-size(17);
}
}
}
.icon-close {
min-width: 22px;
border-radius: 6px;
vertical-align: middle;
cursor: pointer;
@include transition(150ms);
&:hover {
background: $light_background;
line {
color: inherit;
}
}
}
}
.context-menu {
min-width: 250px;
position: absolute;
z-index: 99;
box-shadow: $shadow;
background: white;
border-radius: 8px;
overflow: hidden;
top: 29px;
&.showed {
display: block;
}
}
.created-at-wrapper {
width: 33%;
display: flex;
text-align: center;
justify-content: center;
p {
display: flex;
align-items: center;
@include font-size(11);
}
}
.navigation-icons {
width: 33%;
text-align: right;
.navigation-tool-wrapper {
margin-left: 28px;
display: inline-block;
vertical-align: middle;
}
.button {
margin-left: 5px;
&:hover {
background: $light_background;
}
}
}
.navigation-panel {
height: 63px;
width: 100%;
padding: 10px 15px;
display: flex;
position: absolute;
z-index: 8;
align-items: center;
background-color: white;
color: $text;
}
@media (max-width: 960px) {
.context-menu {
.name-wrapper {
width: 67%;
}
}
.navigation-icons {
display: none;
}
.navigation-panel {
height: 53px;
padding: 15px;
}
.created-at-wrapper {
display: none;
}
.name-wrapper {
justify-content: space-between;
flex-direction: row-reverse;
width: 100%;
}
}
.dark {
.navigation-panel {
background-color: $dark_mode_background;
color: $dark_mode_text_primary;
.icon-close {
color: $dark_mode_text_primary;
&:hover {
background-color: $dark_mode_background;
}
}
}
.name-wrapper {
.title,
.file-count {
color: $dark_mode_text_primary !important;
}
}
.navigation-icons {
.button:hover {
background: $dark_mode_background;
}
}
}
</style>

View File

@@ -1,5 +1,5 @@
<template>
<div class="video-wrapper">
<div class="max-w-[1080px] max-h-full">
<video
:src="file.data.attributes.file_url"
class="video"
@@ -8,7 +8,6 @@
disablePictureInPicture
playsinline
controls
autoplay
/>
</div>
</template>
@@ -19,40 +18,3 @@ export default {
props: ['file'],
}
</script>
<style lang="scss" scoped>
@import '../../../../sass/vuefilemanager/variables';
.video-wrapper {
max-width: 1080px;
max-height: 100%;
@media (min-width: 1200px) {
& {
max-width: 800px;
}
}
@media (min-width: 1920px) and (max-width: 2560px) {
& {
max-width: 1080px;
}
}
@media (min-width: 2560px) and (max-width: 3840px) {
& {
max-width: 1440px;
}
}
@media (min-width: 3840px) {
& {
max-width: 2160px;
}
}
.video {
max-width: 100%;
max-height: 100%;
align-self: center;
}
}
</style>

View File

@@ -1,5 +1,5 @@
<template>
<div class="hidden h-screen w-96 overflow-y-auto overflow-x-hidden px-2.5 lg:block 2xl:w-104">
<div class="hidden h-screen 2xl:w-[360px] w-[320px] shrink-0 overflow-y-auto overflow-x-hidden px-2.5 lg:block">
<!--Is empty clipboard-->
<div v-if="isEmpty" class="flex h-full items-center justify-center">
<div class="text-center">

View File

@@ -1,23 +1,23 @@
<template>
<button class="button hover-text-theme hover-svg-stroke-theme" :title="action">
<corner-down-right-icon v-if="source === 'move'" size="19" class="hover-text-theme" />
<download-cloud-icon v-if="source === 'download'" size="19" class="hover-text-theme" />
<folder-plus-icon v-if="source === 'folder-plus'" size="19" class="hover-text-theme" />
<user-plus-icon v-if="source === 'user-plus'" size="19" class="hover-text-theme" />
<zoom-in-icon v-if="source === 'zoom-in'" size="19" />
<zoom-out-icon v-if="source === 'zoom-out'" size="19" />
<edit-2-icon v-if="source === 'rename'" size="19" />
<printer-icon v-if="source === 'print'" size="19" />
<trash-2-icon v-if="source === 'trash'" size="19" />
<list-icon v-if="source === 'th-list'" size="19" />
<info-icon v-if="source === 'info'" size="19" />
<grid-icon v-if="source === 'th'" size="19" />
<link-icon v-if="source === 'share'" size="19" />
<x-icon v-if="source === 'close'" size="19" />
<search-icon v-if="source === 'search'" size="19" />
<cloud-off-icon v-if="source === 'shared-off'" size="19" />
<sorting-icon v-if="source === 'preview-sorting'" class="preview-sorting" />
<CloudPlusIcon v-if="source === 'cloud-plus'" class="preview-sorting" />
<button class="group h-[42px] w-[42px] inline-flex items-center justify-center cursor-pointer rounded-lg hover:bg-light-background" :title="action">
<corner-down-right-icon v-if="source === 'move'" size="19" class="vue-feather group-hover-text-theme" />
<download-cloud-icon v-if="source === 'download'" size="19" class="vue-feather group-hover-text-theme" />
<folder-plus-icon v-if="source === 'folder-plus'" size="19" class="vue-feather group-hover-text-theme" />
<user-plus-icon v-if="source === 'user-plus'" size="19" class="vue-feather group-hover-text-theme" />
<zoom-in-icon v-if="source === 'zoom-in'" size="19" class="vue-feather group-hover-text-theme" />
<zoom-out-icon v-if="source === 'zoom-out'" size="19" class="vue-feather group-hover-text-theme" />
<edit-2-icon v-if="source === 'rename'" size="19" class="vue-feather group-hover-text-theme" />
<printer-icon v-if="source === 'print'" size="19" class="vue-feather group-hover-text-theme" />
<trash-2-icon v-if="source === 'trash'" size="19" class="vue-feather group-hover-text-theme" />
<list-icon v-if="source === 'th-list'" size="19" class="vue-feather group-hover-text-theme" />
<info-icon v-if="source === 'info'" size="19" class="vue-feather group-hover-text-theme" />
<grid-icon v-if="source === 'th'" size="19" class="vue-feather group-hover-text-theme" />
<link-icon v-if="source === 'share'" size="19" class="vue-feather group-hover-text-theme" />
<x-icon v-if="source === 'close'" size="19" class="vue-feather group-hover-text-theme" />
<search-icon v-if="source === 'search'" size="19" class="vue-feather group-hover-text-theme" />
<cloud-off-icon v-if="source === 'shared-off'" size="19" class="vue-feather group-hover-text-theme" />
<sorting-icon v-if="source === 'preview-sorting'" class="scale-125 vue-feather group-hover-text-theme" />
<CloudPlusIcon v-if="source === 'cloud-plus'" class="scale-125 vue-feather group-hover-text-theme" />
</button>
</template>
@@ -67,73 +67,4 @@ export default {
XIcon,
},
}
</script>
<style scoped lang="scss">
@import 'resources/sass/vuefilemanager/_variables';
@import 'resources/sass/vuefilemanager/_mixins';
.preview-sorting {
transform: scale(1.3);
}
.button {
height: 42px;
width: 42px;
border-radius: 8px;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0;
text-align: center;
cursor: pointer;
white-space: nowrap;
outline: none;
border: none;
@include transition(150ms);
background: transparent;
svg {
color: inherit;
path,
line,
polyline,
rect,
circle {
color: inherit;
}
}
&:hover {
background: $light_background;
path,
line,
polyline,
rect,
circle {
@include transition(150ms);
color: inherit;
}
}
}
.dark {
.button {
background: transparent;
&:hover {
background: $dark_mode_foreground;
}
path,
line,
polyline,
rect,
circle {
stroke: $dark_mode_text_primary;
}
}
}
</style>
</script>

View File

@@ -55,7 +55,7 @@
@include('vuefilemanager.others.color-template')
</head>
<body class="{{ is_dev() ? 'debug-screens' : '' }}">
<body class="{{ is_dev() ? 'debug-screen' : '' }}">
<div id="app"></div>

3
tailwind.config.js vendored
View File

@@ -50,9 +50,6 @@ module.exports = {
screens: {
'print': {'raw': 'print'},
},
width: {
'104': '28rem',
}
},
},
plugins: [