diff --git a/client/components/app/BookShelfCategorized.vue b/client/components/app/BookShelfCategorized.vue
index 1141224a..6c8d4427 100644
--- a/client/components/app/BookShelfCategorized.vue
+++ b/client/components/app/BookShelfCategorized.vue
@@ -16,10 +16,10 @@
-
+ selectEntity(payload, index)">
{{ shelf.label }}
-
+ selectEntity(payload, index)">
{{ shelf.label }}
@@ -33,7 +33,7 @@
-
+ selectEntity(payload, index)" />
@@ -54,7 +54,8 @@ export default {
keywordFilterTimeout: null,
scannerParseSubtitle: false,
wrapperClientWidth: 0,
- shelves: []
+ shelves: [],
+ lastItemIndexSelected: -1
}
},
computed: {
@@ -87,9 +88,64 @@ export default {
sizeMultiplier() {
var baseSize = this.isCoverSquareAspectRatio ? 192 : 120
return this.bookCoverWidth / baseSize
+ },
+ selectedLibraryItems() {
+ return this.$store.state.selectedLibraryItems || []
}
},
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() {
this.wrapperClientWidth = this.$refs.wrapper ? this.$refs.wrapper.clientWidth : 0
@@ -110,6 +166,12 @@ export default {
console.error('Failed to fetch categories', error)
return []
})
+
+ let totalEntityCount = 0
+ for (const shelf of categories) {
+ shelf.shelfStartIndex = totalEntityCount
+ totalEntityCount += shelf.entities.length
+ }
this.shelves = categories
},
async setShelvesFromSearch() {
diff --git a/client/components/app/BookShelfRow.vue b/client/components/app/BookShelfRow.vue
index 0c41efd7..8a1a0166 100644
--- a/client/components/app/BookShelfRow.vue
+++ b/client/components/app/BookShelfRow.vue
@@ -138,11 +138,8 @@ export default {
})
}
},
- selectItem(libraryItem) {
- this.$store.commit('toggleLibraryItemSelected', libraryItem.id)
- this.$nextTick(() => {
- this.$eventBus.$emit('item-selected', libraryItem)
- })
+ selectItem(payload) {
+ this.$emit('selectEntity', payload)
},
itemSelectedEvt() {
this.updateSelectionMode(this.isSelectionMode)
diff --git a/client/components/app/LazyBookshelf.vue b/client/components/app/LazyBookshelf.vue
index 38412364..13ffe0ef 100644
--- a/client/components/app/LazyBookshelf.vue
+++ b/client/components/app/LazyBookshelf.vue
@@ -61,7 +61,8 @@ export default {
keywordFilter: null,
currScrollTop: 0,
resizeTimeout: null,
- mountWindowWidth: 0
+ mountWindowWidth: 0,
+ lastItemIndexSelected: -1
}
},
watch: {
@@ -212,9 +213,55 @@ export default {
this.updateBookSelectionMode(false)
this.isSelectionMode = false
},
- selectEntity(entity) {
+ selectEntity(entity, shiftKey) {
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
if (this.isSelectionMode !== newIsSelectionMode) {
@@ -229,6 +276,9 @@ export default {
this.entityComponentRefs[key].setSelectionMode(isSelectionMode)
}
}
+ if (!isSelectionMode) {
+ this.lastItemIndexSelected = -1
+ }
},
async fetchEntites(page = 0) {
var startIndex = page * this.booksPerFetch
diff --git a/client/components/cards/LazyBookCard.vue b/client/components/cards/LazyBookCard.vue
index f14e166c..da14c6a0 100644
--- a/client/components/cards/LazyBookCard.vue
+++ b/client/components/cards/LazyBookCard.vue
@@ -719,10 +719,10 @@ export default {
console.log('Got library itemn', libraryItem)
this.store.commit('showEReader', libraryItem)
},
- selectBtnClick() {
+ selectBtnClick(evt) {
if (this.processingBatch) return
this.selected = !this.selected
- this.$emit('select', this.libraryItem)
+ this.$emit('select', { entity: this.libraryItem, shiftKey: evt.shiftKey })
},
async play() {
var eventBus = this.$eventBus || this.$nuxt.$eventBus
diff --git a/client/components/widgets/EpisodeSlider.vue b/client/components/widgets/EpisodeSlider.vue
index dae87a15..cc876119 100644
--- a/client/components/widgets/EpisodeSlider.vue
+++ b/client/components/widgets/EpisodeSlider.vue
@@ -94,11 +94,8 @@ export default {
this.$store.commit('setBookshelfBookIds', itemIds)
this.$store.commit('showEditModal', libraryItem)
},
- selectItem(libraryItem) {
- this.$store.commit('toggleLibraryItemSelected', libraryItem.id)
- this.$nextTick(() => {
- this.$eventBus.$emit('item-selected', libraryItem)
- })
+ selectItem(payload) {
+ this.$emit('selectEntity', payload)
},
itemSelectedEvt() {
this.updateSelectionMode(this.isSelectionMode)
diff --git a/client/components/widgets/ItemSlider.vue b/client/components/widgets/ItemSlider.vue
index fea2d06d..7a0bd352 100644
--- a/client/components/widgets/ItemSlider.vue
+++ b/client/components/widgets/ItemSlider.vue
@@ -74,11 +74,8 @@ export default {
this.$store.commit('setBookshelfBookIds', itemIds)
this.$store.commit('showEditModal', libraryItem)
},
- selectItem(libraryItem) {
- this.$store.commit('toggleLibraryItemSelected', libraryItem.id)
- this.$nextTick(() => {
- this.$eventBus.$emit('item-selected', libraryItem)
- })
+ selectItem(payload) {
+ this.$emit('selectEntity', payload)
},
itemSelectedEvt() {
this.updateSelectionMode(this.isSelectionMode)
diff --git a/client/mixins/bookshelfCardsHelpers.js b/client/mixins/bookshelfCardsHelpers.js
index 7d4cd65c..574dee59 100644
--- a/client/mixins/bookshelfCardsHelpers.js
+++ b/client/mixins/bookshelfCardsHelpers.js
@@ -68,8 +68,8 @@ export default {
this.$on('edit', (entity) => {
if (_this.editEntity) _this.editEntity(entity)
})
- this.$on('select', (entity) => {
- if (_this.selectEntity) _this.selectEntity(entity)
+ this.$on('select', ({ entity, shiftKey }) => {
+ if (_this.selectEntity) _this.selectEntity(entity, shiftKey)
})
}
})