mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-01-03 00:06:46 +01:00
Fix:Multi select dropdown menus overflow and update on scroll,Fix: Batch edit only submit updates that were made #222
This commit is contained in:
parent
c17af1fc28
commit
fde6700a82
@ -57,6 +57,12 @@ export default {
|
||||
menu: null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
showMenu(newVal) {
|
||||
if (newVal) this.setListener()
|
||||
else this.removeListener()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
selected: {
|
||||
get() {
|
||||
@ -99,7 +105,18 @@ export default {
|
||||
recalcMenuPos() {
|
||||
if (!this.menu) return
|
||||
var boundingBox = this.$refs.inputWrapper.getBoundingClientRect()
|
||||
this.menu.style.top = boundingBox.y + boundingBox.height - 4 + 'px'
|
||||
if (boundingBox.y > window.innerHeight - 8) {
|
||||
// Input is off the page
|
||||
return this.forceBlur()
|
||||
}
|
||||
var menuHeight = this.menu.clientHeight
|
||||
var top = boundingBox.y + boundingBox.height - 4
|
||||
if (top + menuHeight > window.innerHeight - 20) {
|
||||
// Reverse menu to open upwards
|
||||
top = boundingBox.y - menuHeight - 4
|
||||
}
|
||||
|
||||
this.menu.style.top = top + 'px'
|
||||
this.menu.style.left = boundingBox.x + 'px'
|
||||
this.menu.style.width = boundingBox.width + 'px'
|
||||
},
|
||||
@ -119,7 +136,7 @@ export default {
|
||||
this.unmountMountMenu()
|
||||
}
|
||||
this.isFocused = true
|
||||
this.recalcMenuPos()
|
||||
this.$nextTick(this.recalcMenuPos)
|
||||
},
|
||||
inputBlur() {
|
||||
if (!this.isFocused) return
|
||||
@ -200,6 +217,15 @@ export default {
|
||||
} else {
|
||||
this.insertNewItem(this.textInput)
|
||||
}
|
||||
},
|
||||
scroll() {
|
||||
this.recalcMenuPos()
|
||||
},
|
||||
setListener() {
|
||||
document.addEventListener('scroll', this.scroll, true)
|
||||
},
|
||||
removeListener() {
|
||||
document.removeEventListener('scroll', this.scroll, true)
|
||||
}
|
||||
},
|
||||
mounted() {}
|
||||
|
@ -103,9 +103,12 @@ export default {
|
||||
},
|
||||
closeMenu() {
|
||||
this.showMenu = false
|
||||
this.removeListener()
|
||||
},
|
||||
clickWrapper() {
|
||||
this.showMenu = !this.showMenu
|
||||
if (this.showMenu) this.setListener()
|
||||
else this.removeListener()
|
||||
},
|
||||
removeItem(itemValue) {
|
||||
var remaining = this.selected.filter((i) => i !== itemValue)
|
||||
@ -113,6 +116,15 @@ export default {
|
||||
this.$nextTick(() => {
|
||||
this.recalcMenuPos()
|
||||
})
|
||||
},
|
||||
scroll() {
|
||||
this.recalcMenuPos()
|
||||
},
|
||||
setListener() {
|
||||
document.addEventListener('scroll', this.scroll, true)
|
||||
},
|
||||
removeListener() {
|
||||
document.removeEventListener('scroll', this.scroll, true)
|
||||
}
|
||||
},
|
||||
mounted() {}
|
||||
|
@ -55,7 +55,7 @@
|
||||
|
||||
<div :class="isScrollable ? 'fixed left-0 box-shadow-lg-up bg-primary' : ''" class="w-full h-20 px-4 flex items-center border-t border-bg z-40" :style="{ bottom: streamAudiobook ? '165px' : '0px' }">
|
||||
<div class="flex-grow" />
|
||||
<ui-btn color="success" :padding-x="8" class="text-lg" :loading="isProcessing" @click="saveClick">Save</ui-btn>
|
||||
<ui-btn color="success" :padding-x="8" class="text-lg" :loading="isProcessing" @click.prevent="saveClick">Save</ui-btn>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -178,11 +178,55 @@ export default {
|
||||
}
|
||||
})
|
||||
},
|
||||
compareStringArrays(arr1, arr2) {
|
||||
if (!arr1 || !arr2) return false
|
||||
return arr1.join(',') !== arr2.join(',')
|
||||
},
|
||||
compareAudiobooks(newAb, origAb) {
|
||||
const bookKeysToCheck = ['title', 'subtitle', 'narrator', 'author', 'publishYear', 'series', 'volumeNumber', 'description']
|
||||
var newBook = newAb.book
|
||||
var origBook = origAb.book
|
||||
var diffObj = {}
|
||||
for (const key in newBook) {
|
||||
if (bookKeysToCheck.includes(key)) {
|
||||
if (newBook[key] !== origBook[key]) {
|
||||
if (!diffObj.book) diffObj.book = {}
|
||||
diffObj.book[key] = newBook[key]
|
||||
}
|
||||
}
|
||||
if (key === 'genres') {
|
||||
if (this.compareStringArrays(newBook[key], origBook[key])) {
|
||||
diffObj[key] = newBook[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
if (newAb.tags && origAb.tags && newAb.tags.join(',') !== origAb.tags.join(',')) {
|
||||
diffObj.tags = newAb.tags
|
||||
}
|
||||
return diffObj
|
||||
},
|
||||
saveClick() {
|
||||
this.isProcessing = true
|
||||
var updates = []
|
||||
for (let i = 0; i < this.audiobookCopies.length; i++) {
|
||||
var ab = { ...this.audiobookCopies[i] }
|
||||
var origAb = ab.originalAudiobook
|
||||
delete ab.originalAudiobook
|
||||
|
||||
var res = this.compareAudiobooks(ab, origAb)
|
||||
if (res && Object.keys(res).length) {
|
||||
updates.push({
|
||||
id: ab.id,
|
||||
updates: res
|
||||
})
|
||||
}
|
||||
}
|
||||
if (!updates.length) {
|
||||
return this.$toast.warning('No updates were made')
|
||||
}
|
||||
console.log('Pushing updates', updates)
|
||||
this.isProcessing = true
|
||||
this.$axios
|
||||
.$post('/api/books/batch/update', this.audiobookCopies)
|
||||
.$post('/api/books/batch/update', updates)
|
||||
.then((data) => {
|
||||
this.isProcessing = false
|
||||
if (data.updates) {
|
||||
|
@ -101,42 +101,6 @@ Vue.prototype.$calculateTextSize = (text, styles = {}) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 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__']
|
||||
// }
|
||||
// })
|
||||
|
||||
Vue.prototype.$sanitizeFilename = (input, replacement = '') => {
|
||||
if (typeof input !== 'string') {
|
||||
return false
|
||||
|
@ -99,19 +99,20 @@ class BookController {
|
||||
Logger.warn('User attempted to batch update without permission', req.user)
|
||||
return res.sendStatus(403)
|
||||
}
|
||||
var audiobooks = req.body
|
||||
if (!audiobooks || !audiobooks.length) {
|
||||
var updatePayloads = req.body
|
||||
if (!updatePayloads || !updatePayloads.length) {
|
||||
return res.sendStatus(500)
|
||||
}
|
||||
|
||||
var audiobooksUpdated = 0
|
||||
audiobooks = audiobooks.map((ab) => {
|
||||
var _ab = this.db.audiobooks.find(__ab => __ab.id === ab.id)
|
||||
if (!_ab) return null
|
||||
var hasUpdated = _ab.update(ab)
|
||||
var audiobooks = updatePayloads.map((up) => {
|
||||
var audiobookUpdates = up.updates
|
||||
var ab = this.db.audiobooks.find(_ab => _ab.id === up.id)
|
||||
if (!ab) return null
|
||||
var hasUpdated = ab.update(audiobookUpdates)
|
||||
if (!hasUpdated) return null
|
||||
audiobooksUpdated++
|
||||
return _ab
|
||||
return ab
|
||||
}).filter(ab => ab)
|
||||
|
||||
if (audiobooksUpdated) {
|
||||
|
Loading…
Reference in New Issue
Block a user