mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2024-12-20 19:06:06 +01:00
Add:Support for shift selecting multiple library items #1020
This commit is contained in:
parent
4cf43bc105
commit
77139c7256
@ -16,10 +16,10 @@
|
|||||||
<!-- Alternate plain view -->
|
<!-- Alternate plain view -->
|
||||||
<div v-else-if="isAlternativeBookshelfView" class="w-full mb-24">
|
<div v-else-if="isAlternativeBookshelfView" class="w-full mb-24">
|
||||||
<template v-for="(shelf, index) in shelves">
|
<template v-for="(shelf, index) in shelves">
|
||||||
<widgets-item-slider v-if="shelf.type === 'book' || shelf.type === 'podcast'" :key="index + '.'" :items="shelf.entities" :continue-listening-shelf="shelf.id === 'continue-listening'" :height="232 * sizeMultiplier" class="bookshelf-row pl-8 my-6">
|
<widgets-item-slider v-if="shelf.type === 'book' || shelf.type === 'podcast'" :key="index + '.'" :items="shelf.entities" :continue-listening-shelf="shelf.id === 'continue-listening'" :height="232 * sizeMultiplier" class="bookshelf-row pl-8 my-6" @selectEntity="(payload) => selectEntity(payload, index)">
|
||||||
<p class="font-semibold text-gray-100" :style="{ fontSize: sizeMultiplier + 'rem' }">{{ shelf.label }}</p>
|
<p class="font-semibold text-gray-100" :style="{ fontSize: sizeMultiplier + 'rem' }">{{ shelf.label }}</p>
|
||||||
</widgets-item-slider>
|
</widgets-item-slider>
|
||||||
<widgets-episode-slider v-else-if="shelf.type === 'episode'" :key="index + '.'" :items="shelf.entities" :continue-listening-shelf="shelf.id === 'continue-listening'" :height="232 * sizeMultiplier" class="bookshelf-row pl-8 my-6">
|
<widgets-episode-slider v-else-if="shelf.type === 'episode'" :key="index + '.'" :items="shelf.entities" :continue-listening-shelf="shelf.id === 'continue-listening'" :height="232 * sizeMultiplier" class="bookshelf-row pl-8 my-6" @selectEntity="(payload) => selectEntity(payload, index)">
|
||||||
<p class="font-semibold text-gray-100" :style="{ fontSize: sizeMultiplier + 'rem' }">{{ shelf.label }}</p>
|
<p class="font-semibold text-gray-100" :style="{ fontSize: sizeMultiplier + 'rem' }">{{ shelf.label }}</p>
|
||||||
</widgets-episode-slider>
|
</widgets-episode-slider>
|
||||||
<widgets-series-slider v-else-if="shelf.type === 'series'" :key="index + '.'" :items="shelf.entities" :height="232 * sizeMultiplier" class="bookshelf-row pl-8 my-6">
|
<widgets-series-slider v-else-if="shelf.type === 'series'" :key="index + '.'" :items="shelf.entities" :height="232 * sizeMultiplier" class="bookshelf-row pl-8 my-6">
|
||||||
@ -33,7 +33,7 @@
|
|||||||
<!-- Regular bookshelf view -->
|
<!-- Regular bookshelf view -->
|
||||||
<div v-else class="w-full">
|
<div v-else class="w-full">
|
||||||
<template v-for="(shelf, index) in shelves">
|
<template v-for="(shelf, index) in shelves">
|
||||||
<app-book-shelf-row :key="index" :index="index" :shelf="shelf" :size-multiplier="sizeMultiplier" :book-cover-width="bookCoverWidth" :book-cover-aspect-ratio="coverAspectRatio" :continue-listening-shelf="shelf.id === 'continue-listening'" />
|
<app-book-shelf-row :key="index" :index="index" :shelf="shelf" :size-multiplier="sizeMultiplier" :book-cover-width="bookCoverWidth" :book-cover-aspect-ratio="coverAspectRatio" :continue-listening-shelf="shelf.id === 'continue-listening'" @selectEntity="(payload) => selectEntity(payload, index)" />
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -54,7 +54,8 @@ export default {
|
|||||||
keywordFilterTimeout: null,
|
keywordFilterTimeout: null,
|
||||||
scannerParseSubtitle: false,
|
scannerParseSubtitle: false,
|
||||||
wrapperClientWidth: 0,
|
wrapperClientWidth: 0,
|
||||||
shelves: []
|
shelves: [],
|
||||||
|
lastItemIndexSelected: -1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -87,9 +88,64 @@ export default {
|
|||||||
sizeMultiplier() {
|
sizeMultiplier() {
|
||||||
var baseSize = this.isCoverSquareAspectRatio ? 192 : 120
|
var baseSize = this.isCoverSquareAspectRatio ? 192 : 120
|
||||||
return this.bookCoverWidth / baseSize
|
return this.bookCoverWidth / baseSize
|
||||||
|
},
|
||||||
|
selectedLibraryItems() {
|
||||||
|
return this.$store.state.selectedLibraryItems || []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
selectEntity({ entity, shiftKey }, shelfIndex) {
|
||||||
|
const shelf = this.shelves[shelfIndex]
|
||||||
|
const entityShelfIndex = shelf.entities.findIndex((ent) => ent.id === entity.id)
|
||||||
|
const indexOf = shelf.shelfStartIndex + entityShelfIndex
|
||||||
|
|
||||||
|
const lastLastItemIndexSelected = this.lastItemIndexSelected
|
||||||
|
if (!this.selectedLibraryItems.includes(entity.id)) {
|
||||||
|
this.lastItemIndexSelected = indexOf
|
||||||
|
} else {
|
||||||
|
this.lastItemIndexSelected = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shiftKey && lastLastItemIndexSelected >= 0) {
|
||||||
|
var loopStart = indexOf
|
||||||
|
var loopEnd = lastLastItemIndexSelected
|
||||||
|
if (indexOf > lastLastItemIndexSelected) {
|
||||||
|
loopStart = lastLastItemIndexSelected
|
||||||
|
loopEnd = indexOf
|
||||||
|
}
|
||||||
|
|
||||||
|
const flattenedEntitiesArray = []
|
||||||
|
this.shelves.map((s) => flattenedEntitiesArray.push(...s.entities))
|
||||||
|
|
||||||
|
var isSelecting = false
|
||||||
|
// If any items in this range is not selected then select all otherwise unselect all
|
||||||
|
for (let i = loopStart; i <= loopEnd; i++) {
|
||||||
|
const thisEntity = flattenedEntitiesArray[i]
|
||||||
|
if (thisEntity) {
|
||||||
|
if (!this.selectedLibraryItems.includes(thisEntity.id)) {
|
||||||
|
isSelecting = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isSelecting) this.lastItemIndexSelected = indexOf
|
||||||
|
|
||||||
|
for (let i = loopStart; i <= loopEnd; i++) {
|
||||||
|
const thisEntity = flattenedEntitiesArray[i]
|
||||||
|
if (thisEntity) {
|
||||||
|
this.$store.commit('setLibraryItemSelected', { libraryItemId: thisEntity.id, selected: isSelecting })
|
||||||
|
} else {
|
||||||
|
console.error('Invalid entity index', i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.$store.commit('toggleLibraryItemSelected', entity.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$eventBus.$emit('item-selected', entity)
|
||||||
|
})
|
||||||
|
},
|
||||||
async init() {
|
async init() {
|
||||||
this.wrapperClientWidth = this.$refs.wrapper ? this.$refs.wrapper.clientWidth : 0
|
this.wrapperClientWidth = this.$refs.wrapper ? this.$refs.wrapper.clientWidth : 0
|
||||||
|
|
||||||
@ -110,6 +166,12 @@ export default {
|
|||||||
console.error('Failed to fetch categories', error)
|
console.error('Failed to fetch categories', error)
|
||||||
return []
|
return []
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let totalEntityCount = 0
|
||||||
|
for (const shelf of categories) {
|
||||||
|
shelf.shelfStartIndex = totalEntityCount
|
||||||
|
totalEntityCount += shelf.entities.length
|
||||||
|
}
|
||||||
this.shelves = categories
|
this.shelves = categories
|
||||||
},
|
},
|
||||||
async setShelvesFromSearch() {
|
async setShelvesFromSearch() {
|
||||||
|
@ -138,11 +138,8 @@ export default {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
selectItem(libraryItem) {
|
selectItem(payload) {
|
||||||
this.$store.commit('toggleLibraryItemSelected', libraryItem.id)
|
this.$emit('selectEntity', payload)
|
||||||
this.$nextTick(() => {
|
|
||||||
this.$eventBus.$emit('item-selected', libraryItem)
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
itemSelectedEvt() {
|
itemSelectedEvt() {
|
||||||
this.updateSelectionMode(this.isSelectionMode)
|
this.updateSelectionMode(this.isSelectionMode)
|
||||||
|
@ -61,7 +61,8 @@ export default {
|
|||||||
keywordFilter: null,
|
keywordFilter: null,
|
||||||
currScrollTop: 0,
|
currScrollTop: 0,
|
||||||
resizeTimeout: null,
|
resizeTimeout: null,
|
||||||
mountWindowWidth: 0
|
mountWindowWidth: 0,
|
||||||
|
lastItemIndexSelected: -1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@ -212,9 +213,55 @@ export default {
|
|||||||
this.updateBookSelectionMode(false)
|
this.updateBookSelectionMode(false)
|
||||||
this.isSelectionMode = false
|
this.isSelectionMode = false
|
||||||
},
|
},
|
||||||
selectEntity(entity) {
|
selectEntity(entity, shiftKey) {
|
||||||
if (this.entityName === 'books' || this.entityName === 'series-books') {
|
if (this.entityName === 'books' || this.entityName === 'series-books') {
|
||||||
this.$store.commit('toggleLibraryItemSelected', entity.id)
|
var indexOf = this.entities.findIndex((ent) => ent && ent.id === entity.id)
|
||||||
|
const lastLastItemIndexSelected = this.lastItemIndexSelected
|
||||||
|
if (!this.selectedLibraryItems.includes(entity.id)) {
|
||||||
|
this.lastItemIndexSelected = indexOf
|
||||||
|
} else {
|
||||||
|
this.lastItemIndexSelected = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shiftKey && lastLastItemIndexSelected >= 0) {
|
||||||
|
var loopStart = indexOf
|
||||||
|
var loopEnd = lastLastItemIndexSelected
|
||||||
|
if (indexOf > lastLastItemIndexSelected) {
|
||||||
|
loopStart = lastLastItemIndexSelected
|
||||||
|
loopEnd = indexOf
|
||||||
|
}
|
||||||
|
|
||||||
|
var isSelecting = false
|
||||||
|
// If any items in this range is not selected then select all otherwise unselect all
|
||||||
|
for (let i = loopStart; i <= loopEnd; i++) {
|
||||||
|
const thisEntity = this.entities[i]
|
||||||
|
if (thisEntity && !thisEntity.collapsedSeries) {
|
||||||
|
if (!this.selectedLibraryItems.includes(thisEntity.id)) {
|
||||||
|
isSelecting = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isSelecting) this.lastItemIndexSelected = indexOf
|
||||||
|
|
||||||
|
for (let i = loopStart; i <= loopEnd; i++) {
|
||||||
|
const thisEntity = this.entities[i]
|
||||||
|
if (thisEntity.collapsedSeries) {
|
||||||
|
console.warn('Ignoring collapsed series')
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const entityComponentRef = this.entityComponentRefs[i]
|
||||||
|
if (thisEntity && entityComponentRef) {
|
||||||
|
entityComponentRef.selected = isSelecting
|
||||||
|
this.$store.commit('setLibraryItemSelected', { libraryItemId: thisEntity.id, selected: isSelecting })
|
||||||
|
} else {
|
||||||
|
console.error('Invalid entity index', i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.$store.commit('toggleLibraryItemSelected', entity.id)
|
||||||
|
}
|
||||||
|
|
||||||
var newIsSelectionMode = !!this.selectedLibraryItems.length
|
var newIsSelectionMode = !!this.selectedLibraryItems.length
|
||||||
if (this.isSelectionMode !== newIsSelectionMode) {
|
if (this.isSelectionMode !== newIsSelectionMode) {
|
||||||
@ -229,6 +276,9 @@ export default {
|
|||||||
this.entityComponentRefs[key].setSelectionMode(isSelectionMode)
|
this.entityComponentRefs[key].setSelectionMode(isSelectionMode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!isSelectionMode) {
|
||||||
|
this.lastItemIndexSelected = -1
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async fetchEntites(page = 0) {
|
async fetchEntites(page = 0) {
|
||||||
var startIndex = page * this.booksPerFetch
|
var startIndex = page * this.booksPerFetch
|
||||||
|
@ -719,10 +719,10 @@ export default {
|
|||||||
console.log('Got library itemn', libraryItem)
|
console.log('Got library itemn', libraryItem)
|
||||||
this.store.commit('showEReader', libraryItem)
|
this.store.commit('showEReader', libraryItem)
|
||||||
},
|
},
|
||||||
selectBtnClick() {
|
selectBtnClick(evt) {
|
||||||
if (this.processingBatch) return
|
if (this.processingBatch) return
|
||||||
this.selected = !this.selected
|
this.selected = !this.selected
|
||||||
this.$emit('select', this.libraryItem)
|
this.$emit('select', { entity: this.libraryItem, shiftKey: evt.shiftKey })
|
||||||
},
|
},
|
||||||
async play() {
|
async play() {
|
||||||
var eventBus = this.$eventBus || this.$nuxt.$eventBus
|
var eventBus = this.$eventBus || this.$nuxt.$eventBus
|
||||||
|
@ -94,11 +94,8 @@ export default {
|
|||||||
this.$store.commit('setBookshelfBookIds', itemIds)
|
this.$store.commit('setBookshelfBookIds', itemIds)
|
||||||
this.$store.commit('showEditModal', libraryItem)
|
this.$store.commit('showEditModal', libraryItem)
|
||||||
},
|
},
|
||||||
selectItem(libraryItem) {
|
selectItem(payload) {
|
||||||
this.$store.commit('toggleLibraryItemSelected', libraryItem.id)
|
this.$emit('selectEntity', payload)
|
||||||
this.$nextTick(() => {
|
|
||||||
this.$eventBus.$emit('item-selected', libraryItem)
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
itemSelectedEvt() {
|
itemSelectedEvt() {
|
||||||
this.updateSelectionMode(this.isSelectionMode)
|
this.updateSelectionMode(this.isSelectionMode)
|
||||||
|
@ -74,11 +74,8 @@ export default {
|
|||||||
this.$store.commit('setBookshelfBookIds', itemIds)
|
this.$store.commit('setBookshelfBookIds', itemIds)
|
||||||
this.$store.commit('showEditModal', libraryItem)
|
this.$store.commit('showEditModal', libraryItem)
|
||||||
},
|
},
|
||||||
selectItem(libraryItem) {
|
selectItem(payload) {
|
||||||
this.$store.commit('toggleLibraryItemSelected', libraryItem.id)
|
this.$emit('selectEntity', payload)
|
||||||
this.$nextTick(() => {
|
|
||||||
this.$eventBus.$emit('item-selected', libraryItem)
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
itemSelectedEvt() {
|
itemSelectedEvt() {
|
||||||
this.updateSelectionMode(this.isSelectionMode)
|
this.updateSelectionMode(this.isSelectionMode)
|
||||||
|
@ -68,8 +68,8 @@ export default {
|
|||||||
this.$on('edit', (entity) => {
|
this.$on('edit', (entity) => {
|
||||||
if (_this.editEntity) _this.editEntity(entity)
|
if (_this.editEntity) _this.editEntity(entity)
|
||||||
})
|
})
|
||||||
this.$on('select', (entity) => {
|
this.$on('select', ({ entity, shiftKey }) => {
|
||||||
if (_this.selectEntity) _this.selectEntity(entity)
|
if (_this.selectEntity) _this.selectEntity(entity, shiftKey)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user