mirror of
https://github.com/wekan/wekan.git
synced 2025-12-18 00:10:13 +01: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 {
|
.attachment-upload {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
@ -83,29 +78,56 @@
|
||||||
top: 48px; /* height of the navbar */
|
top: 48px; /* height of the navbar */
|
||||||
left: 0;
|
left: 0;
|
||||||
z-index: 9999 !important;
|
z-index: 9999 !important;
|
||||||
background: rgba(13,13,13,0.9);
|
background: rgba(13,13,13,0.95);
|
||||||
}
|
}
|
||||||
#viewer-container {
|
#viewer-container {
|
||||||
position: relative;
|
display: flex;
|
||||||
width: 100%;
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
height: 100%;
|
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 {
|
#viewer-close {
|
||||||
color:white;
|
color:white;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 4em;
|
font-size: 4em;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 16px;
|
right: 8px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
padding: 20 20;
|
|
||||||
}
|
}
|
||||||
#viewer-container {
|
.attachment-arrow {
|
||||||
text-align: center;
|
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 {
|
#pdf-viewer {
|
||||||
width: 40vw;
|
width: 40vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
}
|
}
|
||||||
|
#txt-viewer{
|
||||||
|
background-color: white;
|
||||||
|
width: 40vw;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
.pdf-preview-error {
|
.pdf-preview-error {
|
||||||
margin-top: 20vh;
|
margin-top: 20vh;
|
||||||
display: block;
|
display: block;
|
||||||
|
|
@ -120,8 +142,32 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 800px) {
|
@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 {
|
#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 {
|
.attachment-thumbnail-container {
|
||||||
width: 100px;
|
width: 100px;
|
||||||
|
|
|
||||||
|
|
@ -32,11 +32,21 @@ template(name="attachmentDeletePopup")
|
||||||
|
|
||||||
template(name="attachmentViewer")
|
template(name="attachmentViewer")
|
||||||
#viewer-overlay.hidden
|
#viewer-overlay.hidden
|
||||||
#viewer-container
|
#viewer-top-bar
|
||||||
object#pdf-viewer(type="application/pdf")
|
span#attachment-name
|
||||||
span.pdf-preview-error {{_ 'preview-pdf-not-supported' }}
|
|
||||||
a#viewer-close.fa.fa-times-thin
|
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")
|
template(name="attachmentGallery")
|
||||||
|
|
||||||
.attachment-gallery
|
.attachment-gallery
|
||||||
|
|
@ -47,7 +57,7 @@ template(name="attachmentGallery")
|
||||||
each attachments
|
each attachments
|
||||||
|
|
||||||
.attachment-item
|
.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 link
|
||||||
if(isImage)
|
if(isImage)
|
||||||
img.attachment-thumbnail(src="{{link}}" title="{{sanitize name}}")
|
img.attachment-thumbnail(src="{{link}}" title="{{sanitize name}}")
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,19 @@ import DOMPurify from 'dompurify';
|
||||||
const filesize = require('filesize');
|
const filesize = require('filesize');
|
||||||
const prettyMilliseconds = require('pretty-ms');
|
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({
|
Template.attachmentGallery.events({
|
||||||
'click .pdf'(event) {
|
'click .open-preview'(event) {
|
||||||
let link = $(event.currentTarget).attr("href");
|
|
||||||
$("#pdf-viewer").attr("data", link);
|
openAttachmentId = $(event.currentTarget).attr("data-attachment-id");
|
||||||
$("#viewer-overlay").removeClass("hidden");
|
cardId = $(event.currentTarget).attr("data-card-id");
|
||||||
|
|
||||||
|
openAttachmentViewer(openAttachmentId);
|
||||||
},
|
},
|
||||||
'click .js-add-attachment': Popup.open('cardAttachments'),
|
'click .js-add-attachment': Popup.open('cardAttachments'),
|
||||||
// If we let this event bubble, FlowRouter will handle it and empty the page
|
// 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({
|
Template.attachmentViewer.events({
|
||||||
'click #viewer-container'(event) {
|
'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) {
|
'click #viewer-content'(event) {
|
||||||
$("#viewer-overlay").addClass("hidden");
|
|
||||||
|
// 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({
|
Template.attachmentGallery.helpers({
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue