mirror of
https://github.com/wekan/wekan.git
synced 2025-04-20 12:07:11 -04:00
Better attachment viewer
This commit is contained in:
parent
7ef3bba9f7
commit
7a98445370
3 changed files with 204 additions and 24 deletions
|
@ -1,8 +1,3 @@
|
|||
.slide {
|
||||
/* swipebox slide background gradient of black to blue, so that back SVG images are visible */
|
||||
background: rgb(2,0,36);
|
||||
background: linear-gradient(90deg, rgba(2,0,36,1) 0%, rgba(9,9,121,1) 14%, rgba(0,212,255,1) 100%);
|
||||
}
|
||||
.attachment-upload {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
|
@ -83,29 +78,56 @@
|
|||
top: 48px; /* height of the navbar */
|
||||
left: 0;
|
||||
z-index: 9999 !important;
|
||||
background: rgba(13,13,13,0.9);
|
||||
background: rgba(13,13,13,0.95);
|
||||
}
|
||||
#viewer-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
height: 100%;
|
||||
}
|
||||
#viewer-top-bar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
padding: 16px;
|
||||
}
|
||||
#attachment-name {
|
||||
color: white;
|
||||
font-size: 1.5em;
|
||||
max-width: calc(100% - 50px); /* Make sure the name does not overlap the close button */
|
||||
}
|
||||
#viewer-close {
|
||||
color:white;
|
||||
cursor: pointer;
|
||||
font-size: 4em;
|
||||
top: 0;
|
||||
right: 16px;
|
||||
right: 8px;
|
||||
position: absolute;
|
||||
padding: 20 20;
|
||||
}
|
||||
#viewer-container {
|
||||
text-align: center;
|
||||
.attachment-arrow {
|
||||
font-size: 4em;
|
||||
color:white;
|
||||
cursor: pointer;
|
||||
align-self: center;
|
||||
margin: 0 20px;
|
||||
}
|
||||
#image-viewer {
|
||||
background:
|
||||
repeating-conic-gradient(#808080 0% 25%, transparent 0% 50%)
|
||||
50% / 20px 20px; /* Checkerboard background for transparent images */
|
||||
max-width: 100%;
|
||||
}
|
||||
#pdf-viewer {
|
||||
width: 40vw;
|
||||
height: 100vh;
|
||||
}
|
||||
#txt-viewer{
|
||||
background-color: white;
|
||||
width: 40vw;
|
||||
height: 100vh;
|
||||
}
|
||||
.pdf-preview-error {
|
||||
margin-top: 20vh;
|
||||
display: block;
|
||||
|
@ -120,8 +142,32 @@
|
|||
}
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
#viewer-container {
|
||||
display: block;
|
||||
}
|
||||
.attachment-arrow{
|
||||
position: absolute;
|
||||
bottom: 2.2em;
|
||||
font-size: 1.6em;
|
||||
padding: 16px;
|
||||
}
|
||||
#prev-attachment{
|
||||
left: 0;
|
||||
}
|
||||
#next-attachment{
|
||||
right: 0;
|
||||
}
|
||||
#pdf-viewer {
|
||||
width: 100vh;
|
||||
width: 100%;
|
||||
height: calc(100vh - 155px); /* Full height - height of top and bottom bars */
|
||||
}
|
||||
#txt-viewer {
|
||||
width: 100%;
|
||||
height: calc(100vh - 155px); /* Full height - height of top and bottom bars */
|
||||
}
|
||||
#audio-viewer {
|
||||
margin-top: 20%;
|
||||
width: 100%;
|
||||
}
|
||||
.attachment-thumbnail-container {
|
||||
width: 100px;
|
||||
|
|
|
@ -32,11 +32,21 @@ template(name="attachmentDeletePopup")
|
|||
|
||||
template(name="attachmentViewer")
|
||||
#viewer-overlay.hidden
|
||||
#viewer-container
|
||||
object#pdf-viewer(type="application/pdf")
|
||||
span.pdf-preview-error {{_ 'preview-pdf-not-supported' }}
|
||||
#viewer-top-bar
|
||||
span#attachment-name
|
||||
a#viewer-close.fa.fa-times-thin
|
||||
|
||||
#viewer-container
|
||||
i.fa.fa-chevron-left.attachment-arrow#prev-attachment
|
||||
#viewer-content
|
||||
img#image-viewer.hidden
|
||||
video#video-viewer.hidden(controls="true")
|
||||
audio#audio-viewer.hidden(controls="true")
|
||||
object#pdf-viewer.hidden(type="application/pdf")
|
||||
span.pdf-preview-error {{_ 'preview-pdf-not-supported' }}
|
||||
object#txt-viewer.hidden(type="text/plain")
|
||||
i.fa.fa-chevron-right.attachment-arrow#next-attachment
|
||||
|
||||
template(name="attachmentGallery")
|
||||
|
||||
.attachment-gallery
|
||||
|
@ -47,7 +57,7 @@ template(name="attachmentGallery")
|
|||
each attachments
|
||||
|
||||
.attachment-item
|
||||
.attachment-thumbnail-container(href="{{link}}" class="{{#if isImage}}swipebox{{/if}} {{#if $eq extension 'pdf'}}pdf{{/if}}")
|
||||
.attachment-thumbnail-container.open-preview(data-attachment-id="{{_id}}" data-card-id="{{ meta.cardId }}")
|
||||
if link
|
||||
if(isImage)
|
||||
img.attachment-thumbnail(src="{{link}}" title="{{sanitize name}}")
|
||||
|
|
|
@ -4,11 +4,19 @@ import DOMPurify from 'dompurify';
|
|||
const filesize = require('filesize');
|
||||
const prettyMilliseconds = require('pretty-ms');
|
||||
|
||||
// We store current card ID and the ID of currently opened attachment in a
|
||||
// global var. This is used so that we know what's the next attachment to open
|
||||
// when the user clicks on the prev/next button in the attachment viewer.
|
||||
let cardId = null;
|
||||
let openAttachmentId = null;
|
||||
|
||||
Template.attachmentGallery.events({
|
||||
'click .pdf'(event) {
|
||||
let link = $(event.currentTarget).attr("href");
|
||||
$("#pdf-viewer").attr("data", link);
|
||||
$("#viewer-overlay").removeClass("hidden");
|
||||
'click .open-preview'(event) {
|
||||
|
||||
openAttachmentId = $(event.currentTarget).attr("data-attachment-id");
|
||||
cardId = $(event.currentTarget).attr("data-card-id");
|
||||
|
||||
openAttachmentViewer(openAttachmentId);
|
||||
},
|
||||
'click .js-add-attachment': Popup.open('cardAttachments'),
|
||||
// If we let this event bubble, FlowRouter will handle it and empty the page
|
||||
|
@ -24,13 +32,129 @@ Template.attachmentGallery.events({
|
|||
}),
|
||||
});
|
||||
|
||||
function getNextAttachmentId(currentAttachmentId) {
|
||||
const attachments = Attachments.find({'meta.cardId': cardId}).get();
|
||||
|
||||
let i = 0;
|
||||
for (; i < attachments.length; i++) {
|
||||
if (attachments[i]._id === currentAttachmentId) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return attachments[(i + 1 + attachments.length) % attachments.length]._id;
|
||||
}
|
||||
|
||||
function getPrevAttachmentId(currentAttachmentId) {
|
||||
const attachments = Attachments.find({'meta.cardId': cardId}).get();
|
||||
|
||||
let i = 0;
|
||||
for (; i < attachments.length; i++) {
|
||||
if (attachments[i]._id === currentAttachmentId) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return attachments[(i - 1 + attachments.length) % attachments.length]._id;
|
||||
}
|
||||
|
||||
function openAttachmentViewer(attachmentId){
|
||||
|
||||
const attachment = Attachments.findOne({_id: attachmentId});
|
||||
|
||||
$("#attachment-name").text(attachment.name);
|
||||
|
||||
// IMPORTANT: if you ever add a new viewer, make sure you also implement
|
||||
// cleanup in the closeAttachmentViewer() function
|
||||
switch(true){
|
||||
case (attachment.isImage):
|
||||
$("#image-viewer").attr("src", attachment.link());
|
||||
$("#image-viewer").removeClass("hidden");
|
||||
break;
|
||||
case (attachment.isPDF):
|
||||
$("#pdf-viewer").attr("data", attachment.link());
|
||||
$("#pdf-viewer").removeClass("hidden");
|
||||
break;
|
||||
case (attachment.isVideo):
|
||||
// We have to create a new <source> DOM element and append it to the video
|
||||
// element, otherwise the video won't load
|
||||
let videoSource = document.createElement('source');
|
||||
videoSource.setAttribute('src', attachment.link());
|
||||
$("#video-viewer").append(videoSource);
|
||||
|
||||
$("#video-viewer").removeClass("hidden");
|
||||
break;
|
||||
case (attachment.isAudio):
|
||||
// We have to create a new <source> DOM element and append it to the audio
|
||||
// element, otherwise the audio won't load
|
||||
let audioSource = document.createElement('source');
|
||||
audioSource.setAttribute('src', attachment.link());
|
||||
$("#audio-viewer").append(audioSource);
|
||||
|
||||
$("#audio-viewer").removeClass("hidden");
|
||||
break;
|
||||
case (attachment.isText):
|
||||
case (attachment.isJSON):
|
||||
$("#txt-viewer").attr("data", attachment.link());
|
||||
$("#txt-viewer").removeClass("hidden");
|
||||
break;
|
||||
}
|
||||
|
||||
$("#viewer-overlay").removeClass("hidden");
|
||||
}
|
||||
|
||||
function closeAttachmentViewer() {
|
||||
$("#viewer-overlay").addClass("hidden");
|
||||
|
||||
// We need to reset the viewers to avoid showing previous attachments
|
||||
$("#image-viewer").attr("src", "");
|
||||
$("#image-viewer").addClass("hidden");
|
||||
|
||||
$("#pdf-viewer").attr("data", "");
|
||||
$("#pdf-viewer").addClass("hidden");
|
||||
|
||||
$("#txt-viewer").attr("data", "");
|
||||
$("#txt-viewer").addClass("hidden");
|
||||
|
||||
$("#video-viewer").get(0).pause(); // Stop playback
|
||||
$("#video-viewer").get(0).currentTime = 0;
|
||||
$("#video-viewer").empty();
|
||||
$("#video-viewer").addClass("hidden");
|
||||
|
||||
$("#audio-viewer").get(0).pause(); // Stop playback
|
||||
$("#audio-viewer").get(0).currentTime = 0;
|
||||
$("#audio-viewer").empty();
|
||||
$("#audio-viewer").addClass("hidden");
|
||||
}
|
||||
|
||||
Template.attachmentViewer.events({
|
||||
'click #viewer-container'(event) {
|
||||
$("#viewer-overlay").addClass("hidden");
|
||||
|
||||
// Make sure the click was on #viewer-container and not on any of its children
|
||||
if(event.target !== event.currentTarget) return;
|
||||
|
||||
closeAttachmentViewer();
|
||||
},
|
||||
'click #viewer-close'(event) {
|
||||
$("#viewer-overlay").addClass("hidden");
|
||||
'click #viewer-content'(event) {
|
||||
|
||||
// Make sure the click was on #viewer-content and not on any of its children
|
||||
if(event.target !== event.currentTarget) return;
|
||||
|
||||
closeAttachmentViewer();
|
||||
},
|
||||
'click #viewer-close'() {
|
||||
closeAttachmentViewer();
|
||||
},
|
||||
'click #next-attachment'(event) {
|
||||
closeAttachmentViewer()
|
||||
const id = getNextAttachmentId(openAttachmentId);
|
||||
openAttachmentId = id;
|
||||
openAttachmentViewer(id);
|
||||
},
|
||||
'click #prev-attachment'(event) {
|
||||
closeAttachmentViewer()
|
||||
const id = getPrevAttachmentId(openAttachmentId);
|
||||
openAttachmentId = id;
|
||||
openAttachmentViewer(id);
|
||||
}
|
||||
});
|
||||
|
||||
Template.attachmentGallery.helpers({
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue