mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2025-06-25 22:58:43 +02:00
Fix ereader padding and swiping
This commit is contained in:
parent
39fbb0902e
commit
bf62b82037
8 changed files with 80 additions and 74 deletions
|
@ -1,11 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="comic-reader" class="w-full h-full">
|
<div id="comic-reader" class="w-full h-full relative">
|
||||||
<div v-show="showPageMenu" v-click-outside="clickOutside" class="pagemenu absolute right-20 rounded-md overflow-y-auto bg-bg shadow-lg z-20 border border-gray-400 w-52" style="top: 72px">
|
<div v-show="showPageMenu" v-click-outside="clickOutsideObj" class="pagemenu absolute top-12 right-16 rounded-md overflow-y-auto bg-bg shadow-lg z-20 border border-gray-400 w-52">
|
||||||
<div v-for="(file, index) in pages" :key="file" class="w-full cursor-pointer hover:bg-black-200 px-2 py-1" :class="page === index ? 'bg-black-200' : ''" @click="setPage(index)">
|
<div v-for="(file, index) in pages" :key="file" class="w-full cursor-pointer hover:bg-black-200 px-2 py-1" :class="page === index ? 'bg-black-200' : ''" @click.stop="setPage(index)">
|
||||||
<p class="text-sm truncate">{{ file }}</p>
|
<p class="text-sm truncate">{{ file }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-show="showInfoMenu" v-click-outside="clickOutside" class="pagemenu absolute top-20 right-0 rounded-md overflow-y-auto bg-bg shadow-lg z-20 border border-gray-400 w-full" style="top: 72px">
|
<div v-show="showInfoMenu" v-click-outside="clickedOutsideInfoMenu" class="pagemenu absolute top-12 right-0 rounded-md overflow-y-auto bg-bg shadow-lg z-20 border border-gray-400 w-full" @click.stop.prevent>
|
||||||
<div v-for="key in comicMetadataKeys" :key="key" class="w-full px-2 py-1">
|
<div v-for="key in comicMetadataKeys" :key="key" class="w-full px-2 py-1">
|
||||||
<p class="text-xs">
|
<p class="text-xs">
|
||||||
<strong>{{ key }}</strong>
|
<strong>{{ key }}</strong>
|
||||||
|
@ -14,13 +14,13 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="comicMetadata" class="absolute top-8 right-36 bg-bg text-gray-100 border-b border-l border-r border-gray-400 hover:bg-black-200 cursor-pointer rounded-b-md w-10 h-9 flex items-center justify-center text-center z-20" @mousedown.prevent @click.stop.prevent="showInfoMenu = !showInfoMenu">
|
<div v-if="comicMetadata" class="absolute top-0 right-3 bg-bg text-gray-100 border-b border-l border-r border-gray-400 hover:bg-black-200 cursor-pointer rounded-b-md w-10 h-9 flex items-center justify-center text-center z-20" @mousedown.prevent @click.stop.prevent="clickShowInfoMenu">
|
||||||
<span class="material-icons text-lg">more</span>
|
<span class="material-icons text-lg">more</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="absolute top-8 bg-bg text-gray-100 border-b border-l border-r border-gray-400 hover:bg-black-200 cursor-pointer rounded-b-md w-10 h-9 flex items-center justify-center text-center z-20" style="right: 92px" @mousedown.prevent @click.stop.prevent="showPageMenu = !showPageMenu">
|
<div class="absolute top-0 right-16 bg-bg text-gray-100 border-b border-l border-r border-gray-400 hover:bg-black-200 cursor-pointer rounded-b-md w-10 h-9 flex items-center justify-center text-center z-20" @mousedown.prevent @mouseup.prevent @click.stop.prevent="clickShowPageMenu">
|
||||||
<span class="material-icons text-lg">menu</span>
|
<span class="material-icons text-lg">menu</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="absolute top-8 right-4 bg-bg text-gray-100 border-b border-l border-r border-gray-400 rounded-b-md px-2 h-9 flex items-center text-center z-20">
|
<div class="absolute top-0 left-3 bg-bg text-gray-100 border-b border-l border-r border-gray-400 rounded-b-md px-2 h-9 flex items-center text-center z-20" @click.stop>
|
||||||
<p class="font-mono">{{ page + 1 }} / {{ numPages }}</p>
|
<p class="font-mono">{{ page + 1 }} / {{ numPages }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -61,7 +61,12 @@ export default {
|
||||||
showInfoMenu: false,
|
showInfoMenu: false,
|
||||||
loadTimeout: null,
|
loadTimeout: null,
|
||||||
loadedFirstPage: false,
|
loadedFirstPage: false,
|
||||||
comicMetadata: null
|
comicMetadata: null,
|
||||||
|
clickOutsideObj: {
|
||||||
|
handler: this.clickedOutside,
|
||||||
|
events: ['mousedown'],
|
||||||
|
isActive: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -84,9 +89,19 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
clickOutside() {
|
clickShowInfoMenu() {
|
||||||
if (this.showPageMenu) this.showPageMenu = false
|
this.showInfoMenu = !this.showInfoMenu
|
||||||
if (this.showInfoMenu) this.showInfoMenu = false
|
this.showPageMenu = false
|
||||||
|
},
|
||||||
|
clickShowPageMenu() {
|
||||||
|
this.showPageMenu = !this.showPageMenu
|
||||||
|
this.showInfoMenu = false
|
||||||
|
},
|
||||||
|
clickedOutside() {
|
||||||
|
this.showPageMenu = false
|
||||||
|
},
|
||||||
|
clickedOutsideInfoMenu() {
|
||||||
|
this.showInfoMenu = false
|
||||||
},
|
},
|
||||||
next() {
|
next() {
|
||||||
if (!this.canGoNext) return
|
if (!this.canGoNext) return
|
||||||
|
@ -100,7 +115,8 @@ export default {
|
||||||
if (index < 0 || index > this.numPages - 1) {
|
if (index < 0 || index > this.numPages - 1) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var filename = this.pages[index]
|
this.showPageMenu = false
|
||||||
|
const filename = this.pages[index]
|
||||||
this.page = index
|
this.page = index
|
||||||
return this.extractFile(filename)
|
return this.extractFile(filename)
|
||||||
},
|
},
|
||||||
|
@ -235,8 +251,16 @@ export default {
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
#comic-reader {
|
#comic-reader {
|
||||||
height: calc(100% - 32px);
|
height: calc(100% - 36px);
|
||||||
|
max-height: calc(100% - 36px);
|
||||||
|
padding-top: 36px;
|
||||||
}
|
}
|
||||||
|
.reader-player-open #comic-reader {
|
||||||
|
height: calc(100% - 156px);
|
||||||
|
max-height: calc(100% - 156px);
|
||||||
|
padding-top: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
.pagemenu {
|
.pagemenu {
|
||||||
max-height: calc(100% - 80px);
|
max-height: calc(100% - 80px);
|
||||||
}
|
}
|
||||||
|
|
|
@ -140,13 +140,13 @@ export default {
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
#epub-frame {
|
#epub-frame {
|
||||||
height: calc(100% - 32px);
|
height: calc(100% - 52px);
|
||||||
max-height: calc(100% - 32px);
|
max-height: calc(100% - 52px);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.reader-player-open #epub-frame {
|
.reader-player-open #epub-frame {
|
||||||
height: calc(100% - 132px);
|
height: calc(100% - 152px);
|
||||||
max-height: calc(100% - 132px);
|
max-height: calc(100% - 152px);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
|
@ -1,9 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="mobi-ebook-viewer w-full">
|
<div class="mobi-ebook-viewer w-full relative">
|
||||||
<div class="absolute overflow-hidden left-0 right-0 w-full max-w-full m-auto z-10 border border-black border-opacity-20 shadow-md bg-white">
|
<div class="absolute overflow-hidden left-0 right-0 w-full max-w-full m-auto z-10 border border-black border-opacity-20 shadow-md bg-white">
|
||||||
<iframe title="html-viewer" class="w-full overflow-hidden"> Loading </iframe>
|
<iframe title="html-viewer" class="w-full overflow-hidden"> Loading </iframe>
|
||||||
</div>
|
</div>
|
||||||
<div class="fixed bottom-0 left-0 h-8 w-full bg-bg px-2 flex items-center z-20" :style="{ bottom: playerLibraryItemId ? '100px' : '0px' }">
|
<div class="fixed bottom-0 left-0 h-8 w-full bg-bg px-2 flex items-center z-20" :style="{ bottom: playerLibraryItemId ? '120px' : '0px' }">
|
||||||
<p class="text-xs">mobi</p>
|
<p class="text-xs">mobi</p>
|
||||||
<div class="flex-grow" />
|
<div class="flex-grow" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -25,9 +25,6 @@ export default {
|
||||||
computed: {
|
computed: {
|
||||||
playerLibraryItemId() {
|
playerLibraryItemId() {
|
||||||
return this.$store.state.playerLibraryItemId
|
return this.$store.state.playerLibraryItemId
|
||||||
},
|
|
||||||
readerHeightOffset() {
|
|
||||||
return this.playerLibraryItemId ? 164 : 64
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -122,13 +119,15 @@ export default {
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.mobi-ebook-viewer {
|
.mobi-ebook-viewer {
|
||||||
height: calc(100% - 32px);
|
height: calc(100% - 52px);
|
||||||
max-height: calc(100% - 32px);
|
max-height: calc(100% - 52px);
|
||||||
overflow: hidden;
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
.reader-player-open .mobi-ebook-viewer {
|
.reader-player-open .mobi-ebook-viewer {
|
||||||
height: calc(100% - 132px);
|
height: calc(100% - 152px);
|
||||||
max-height: calc(100% - 132px);
|
max-height: calc(100% - 152px);
|
||||||
overflow: hidden;
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="w-full h-full pt-20 relative">
|
<div class="w-full h-full pt-6 relative">
|
||||||
<div v-show="canGoPrev" class="absolute top-0 left-0 h-full w-1/2 hover:opacity-100 opacity-0 z-10 cursor-pointer" @click.stop.prevent="prev" @mousedown.prevent>
|
<div v-show="canGoPrev" class="absolute top-0 left-0 h-full w-1/2 hover:opacity-100 opacity-0 z-10 cursor-pointer" @click.stop.prevent="prev" @mousedown.prevent>
|
||||||
<div class="flex items-center justify-center h-full w-1/2">
|
<div class="flex items-center justify-center h-full w-1/2">
|
||||||
<span class="material-icons text-5xl text-white cursor-pointer text-opacity-30 hover:text-opacity-90">arrow_back_ios</span>
|
<span class="material-icons text-5xl text-white cursor-pointer text-opacity-30 hover:text-opacity-90">arrow_back_ios</span>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-if="show" class="absolute top-0 left-0 w-full h-full bg-bg z-40 pt-8" :class="{ 'reader-player-open': !!playerLibraryItemId }">
|
<div v-if="show" class="absolute top-0 left-0 w-full h-full bg-bg z-40 pt-14" :class="{ 'reader-player-open': !!playerLibraryItemId }">
|
||||||
<div class="h-8 w-full bg-primary flex items-center px-2 fixed top-0 left-0 z-30 box-shadow-sm">
|
<div class="h-14 pt-6 w-full bg-primary flex items-center px-2 fixed top-0 left-0 z-30 box-shadow-sm">
|
||||||
<p class="w-5/6 truncate">{{ title }}</p>
|
<p class="w-5/6 truncate">{{ title }}</p>
|
||||||
<div class="flex-grow" />
|
<div class="flex-grow" />
|
||||||
<span class="material-icons text-xl text-white" @click.stop="show = false">close</span>
|
<span class="material-icons text-xl text-white" @click.stop="show = false">close</span>
|
||||||
|
@ -132,7 +132,9 @@ export default {
|
||||||
|
|
||||||
const touchDistanceX = Math.abs(this.touchendX - this.touchstartX)
|
const touchDistanceX = Math.abs(this.touchendX - this.touchstartX)
|
||||||
const touchDistanceY = Math.abs(this.touchendY - this.touchstartY)
|
const touchDistanceY = Math.abs(this.touchendY - this.touchstartY)
|
||||||
if (touchDistanceX < 100 || touchDistanceY > touchDistanceX) return
|
if (touchDistanceX < 60 || touchDistanceY > touchDistanceX) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (this.touchendX < this.touchstartX) {
|
if (this.touchendX < this.touchstartX) {
|
||||||
console.log('swiped left')
|
console.log('swiped left')
|
||||||
|
@ -144,8 +146,8 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
touchstart(e) {
|
touchstart(e) {
|
||||||
this.touchstartX = e.changedTouches[0].screenX
|
this.touchstartX = e.touches[0].screenX
|
||||||
this.touchstartY = e.changedTouches[0].screenY
|
this.touchstartY = e.touches[0].screenY
|
||||||
this.touchstartTime = Date.now()
|
this.touchstartTime = Date.now()
|
||||||
},
|
},
|
||||||
touchend(e) {
|
touchend(e) {
|
||||||
|
|
16
package-lock.json
generated
16
package-lock.json
generated
|
@ -26,6 +26,7 @@
|
||||||
"libarchive.js": "^1.3.0",
|
"libarchive.js": "^1.3.0",
|
||||||
"nuxt": "^2.15.7",
|
"nuxt": "^2.15.7",
|
||||||
"socket.io-client": "^4.1.3",
|
"socket.io-client": "^4.1.3",
|
||||||
|
"v-click-outside": "^3.2.0",
|
||||||
"vue-pdf": "^4.2.0",
|
"vue-pdf": "^4.2.0",
|
||||||
"vue-toastification": "^1.7.11",
|
"vue-toastification": "^1.7.11",
|
||||||
"vuedraggable": "^2.24.3"
|
"vuedraggable": "^2.24.3"
|
||||||
|
@ -17192,6 +17193,14 @@
|
||||||
"node": ">= 0.4.0"
|
"node": ">= 0.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/v-click-outside": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/v-click-outside/-/v-click-outside-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-QD0bDy38SHJXQBjgnllmkI/rbdiwmq9RC+/+pvrFjYJKTn8dtp7Penf9q1lLBta280fYG2q53mgLhQ+3l3z74w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vary": {
|
"node_modules/vary": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
|
@ -31992,6 +32001,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||||
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
|
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
|
||||||
},
|
},
|
||||||
|
"v-click-outside": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/v-click-outside/-/v-click-outside-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-QD0bDy38SHJXQBjgnllmkI/rbdiwmq9RC+/+pvrFjYJKTn8dtp7Penf9q1lLBta280fYG2q53mgLhQ+3l3z74w=="
|
||||||
|
},
|
||||||
"vary": {
|
"vary": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
|
@ -33339,4 +33353,4 @@
|
||||||
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="
|
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
"libarchive.js": "^1.3.0",
|
"libarchive.js": "^1.3.0",
|
||||||
"nuxt": "^2.15.7",
|
"nuxt": "^2.15.7",
|
||||||
"socket.io-client": "^4.1.3",
|
"socket.io-client": "^4.1.3",
|
||||||
|
"v-click-outside": "^3.2.0",
|
||||||
"vue-pdf": "^4.2.0",
|
"vue-pdf": "^4.2.0",
|
||||||
"vue-toastification": "^1.7.11",
|
"vue-toastification": "^1.7.11",
|
||||||
"vuedraggable": "^2.24.3"
|
"vuedraggable": "^2.24.3"
|
||||||
|
@ -41,4 +42,4 @@
|
||||||
"@nuxtjs/tailwindcss": "^4.2.0",
|
"@nuxtjs/tailwindcss": "^4.2.0",
|
||||||
"postcss": "^8.3.5"
|
"postcss": "^8.3.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
|
import vClickOutside from 'v-click-outside'
|
||||||
import { App } from '@capacitor/app'
|
import { App } from '@capacitor/app'
|
||||||
import { Dialog } from '@capacitor/dialog'
|
import { Dialog } from '@capacitor/dialog'
|
||||||
import { StatusBar, Style } from '@capacitor/status-bar';
|
import { StatusBar, Style } from '@capacitor/status-bar';
|
||||||
import { formatDistance, format, addDays, isDate } from 'date-fns'
|
import { formatDistance, format, addDays, isDate } from 'date-fns'
|
||||||
import { Capacitor } from '@capacitor/core';
|
import { Capacitor } from '@capacitor/core'
|
||||||
|
|
||||||
|
Vue.directive('click-outside', vClickOutside.directive)
|
||||||
|
|
||||||
if (Capacitor.getPlatform() != 'web') {
|
if (Capacitor.getPlatform() != 'web') {
|
||||||
const setStatusBarStyleDark = async () => {
|
const setStatusBarStyleDark = async () => {
|
||||||
|
@ -150,43 +153,6 @@ Vue.prototype.$sanitizeFilename = (input, colonReplacement = ' - ') => {
|
||||||
return sanitized
|
return sanitized
|
||||||
}
|
}
|
||||||
|
|
||||||
function isClickedOutsideEl(clickEvent, elToCheckOutside, ignoreSelectors = [], ignoreElems = []) {
|
|
||||||
const isDOMElement = (element) => {
|
|
||||||
return element instanceof Element || element instanceof HTMLDocument
|
|
||||||
}
|
|
||||||
|
|
||||||
const clickedEl = clickEvent.srcElement
|
|
||||||
const didClickOnIgnoredEl = ignoreElems.filter((el) => el).some((element) => element.contains(clickedEl) || element.isEqualNode(clickedEl))
|
|
||||||
const didClickOnIgnoredSelector = ignoreSelectors.length ? ignoreSelectors.map((selector) => clickedEl.closest(selector)).reduce((curr, accumulator) => curr && accumulator, true) : false
|
|
||||||
|
|
||||||
if (isDOMElement(elToCheckOutside) && !elToCheckOutside.contains(clickedEl) && !didClickOnIgnoredEl && !didClickOnIgnoredSelector) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
Vue.directive('click-outside', {
|
|
||||||
bind: function (el, binding, vnode) {
|
|
||||||
let vm = vnode.context;
|
|
||||||
let callback = binding.value;
|
|
||||||
if (typeof callback !== 'function') {
|
|
||||||
console.error('Invalid callback', binding)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
el['__click_outside__'] = (ev) => {
|
|
||||||
if (isClickedOutsideEl(ev, el)) {
|
|
||||||
callback.call(vm, ev)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
document.addEventListener('click', el['__click_outside__'], false)
|
|
||||||
},
|
|
||||||
unbind: function (el, binding, vnode) {
|
|
||||||
document.removeEventListener('click', el['__click_outside__'], false)
|
|
||||||
delete el['__click_outside__']
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
function xmlToJson(xml) {
|
function xmlToJson(xml) {
|
||||||
const json = {};
|
const json = {};
|
||||||
for (const res of xml.matchAll(/(?:<(\w*)(?:\s[^>]*)*>)((?:(?!<\1).)*)(?:<\/\1>)|<(\w*)(?:\s*)*\/>/gm)) {
|
for (const res of xml.matchAll(/(?:<(\w*)(?:\s[^>]*)*>)((?:(?!<\1).)*)(?:<\/\1>)|<(\w*)(?:\s*)*\/>/gm)) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue