mirror of
https://github.com/gotson/komga.git
synced 2026-05-09 05:10:19 +02:00
feat(webui): select multiple items using shift+click
This commit is contained in:
parent
8545574d38
commit
f69a31eaf1
2 changed files with 65 additions and 31 deletions
|
|
@ -14,25 +14,25 @@
|
||||||
:class="flexClass"
|
:class="flexClass"
|
||||||
>
|
>
|
||||||
<v-item
|
<v-item
|
||||||
v-for="item in localItems"
|
v-for="item in localItems"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
class="my-2 mx-2"
|
class="my-2 mx-2"
|
||||||
v-slot:default="{ toggle, active }" :value="item"
|
v-slot:default="{ toggle, active }" :value="item"
|
||||||
>
|
>
|
||||||
<slot name="item">
|
<slot name="item">
|
||||||
<div style="position: relative"
|
<div style="position: relative"
|
||||||
:class="draggable ? 'draggable-item' : undefined"
|
:class="draggable ? 'draggable-item' : undefined"
|
||||||
>
|
>
|
||||||
<item-card
|
<item-card
|
||||||
class="item-card"
|
class="item-card"
|
||||||
:item="item"
|
:item="item"
|
||||||
:width="itemWidth"
|
:width="itemWidth"
|
||||||
:selected="active"
|
:selected="active"
|
||||||
:no-link="draggable || deletable"
|
:no-link="draggable || deletable"
|
||||||
:preselect="shouldPreselect"
|
:preselect="shouldPreselect"
|
||||||
:onEdit="(draggable || deletable) ? undefined : editFunction"
|
:onEdit="(draggable || deletable) ? undefined : editFunction"
|
||||||
:onSelected="(draggable || deletable) ? undefined : selectable ? toggle: undefined"
|
:onSelected="(draggable || deletable) ? undefined : selectable ? (item, event) => handleSelectClick(toggle, item, event): undefined"
|
||||||
:action-menu="actionMenu"
|
:action-menu="actionMenu"
|
||||||
></item-card>
|
></item-card>
|
||||||
|
|
||||||
<v-slide-y-reverse-transition>
|
<v-slide-y-reverse-transition>
|
||||||
|
|
@ -73,13 +73,13 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import ItemCard from '@/components/ItemCard.vue'
|
import ItemCard from '@/components/ItemCard.vue'
|
||||||
import { computeCardWidth } from '@/functions/grid-utilities'
|
import {computeCardWidth} from '@/functions/grid-utilities'
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import draggable from 'vuedraggable'
|
import draggable from 'vuedraggable'
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
name: 'ItemBrowser',
|
name: 'ItemBrowser',
|
||||||
components: { ItemCard, draggable },
|
components: {ItemCard, draggable},
|
||||||
props: {
|
props: {
|
||||||
items: {
|
items: {
|
||||||
type: Array,
|
type: Array,
|
||||||
|
|
@ -122,51 +122,53 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
data: () => {
|
data: () => {
|
||||||
return {
|
return {
|
||||||
selectedItems: [],
|
selectedItems: [] as any[],
|
||||||
localItems: [],
|
localItems: [],
|
||||||
|
lastClickedNoShift: undefined as any,
|
||||||
|
lastClickedShift: undefined as any,
|
||||||
width: 150,
|
width: 150,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
selectedItems: {
|
selectedItems: {
|
||||||
handler () {
|
handler() {
|
||||||
this.$emit('update:selected', this.selectedItems)
|
this.$emit('update:selected', this.selectedItems)
|
||||||
},
|
},
|
||||||
immediate: true,
|
immediate: true,
|
||||||
},
|
},
|
||||||
selected: {
|
selected: {
|
||||||
handler () {
|
handler() {
|
||||||
this.selectedItems = this.selected as []
|
this.selectedItems = this.selected as []
|
||||||
},
|
},
|
||||||
immediate: true,
|
immediate: true,
|
||||||
},
|
},
|
||||||
items: {
|
items: {
|
||||||
handler () {
|
handler() {
|
||||||
this.localItems = this.items as []
|
this.localItems = this.items as []
|
||||||
},
|
},
|
||||||
immediate: true,
|
immediate: true,
|
||||||
},
|
},
|
||||||
localItems: {
|
localItems: {
|
||||||
handler () {
|
handler() {
|
||||||
this.$emit('update:items', this.localItems)
|
this.$emit('update:items', this.localItems)
|
||||||
},
|
},
|
||||||
immediate: true,
|
immediate: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
flexClass (): string {
|
flexClass(): string {
|
||||||
return this.nowrap ? 'd-flex flex-nowrap' : 'd-flex flex-wrap'
|
return this.nowrap ? 'd-flex flex-nowrap' : 'd-flex flex-wrap'
|
||||||
},
|
},
|
||||||
hasItems (): boolean {
|
hasItems(): boolean {
|
||||||
return this.items.length > 0
|
return this.items.length > 0
|
||||||
},
|
},
|
||||||
itemWidth (): number {
|
itemWidth(): number {
|
||||||
return this.fixedItemWidth ? this.fixedItemWidth : this.width
|
return this.fixedItemWidth ? this.fixedItemWidth : this.width
|
||||||
},
|
},
|
||||||
shouldPreselect (): boolean {
|
shouldPreselect(): boolean {
|
||||||
return this.selectedItems.length > 0
|
return this.selectedItems.length > 0
|
||||||
},
|
},
|
||||||
dragOptions (): any {
|
dragOptions(): any {
|
||||||
return {
|
return {
|
||||||
animation: 200,
|
animation: 200,
|
||||||
group: 'item-cards',
|
group: 'item-cards',
|
||||||
|
|
@ -176,11 +178,43 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onResize () {
|
// handle normal click and shift click
|
||||||
|
handleSelectClick(toggle: () => void, item: any, e: MouseEvent) {
|
||||||
|
if (!e.shiftKey) {
|
||||||
|
this.lastClickedShift = undefined
|
||||||
|
this.lastClickedNoShift = item
|
||||||
|
} else {
|
||||||
|
// if it's a shift click and we haven't clicked anywhere before, consider the first item as the beginning of the selection
|
||||||
|
if (!this.lastClickedNoShift) this.lastClickedNoShift = this.$_.head(this.localItems)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.shiftKey && this.lastClickedNoShift) {
|
||||||
|
// recompute last shift selection so we can unselect it
|
||||||
|
if (this.lastClickedShift) {
|
||||||
|
const pf = (i: any) => i !== this.lastClickedShift && i !== this.lastClickedNoShift
|
||||||
|
let previousShiftSelection = this.$_.dropRightWhile(this.$_.dropWhile(this.localItems, pf), pf)
|
||||||
|
this.$_.pullAll(this.selectedItems, previousShiftSelection)
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute shift selection and select it
|
||||||
|
const f = (i: any) => i !== item && i !== this.lastClickedNoShift
|
||||||
|
let shiftSelection = this.$_.dropRightWhile(this.$_.dropWhile(this.localItems, f), f)
|
||||||
|
shiftSelection.forEach(i => {
|
||||||
|
if (!this.selectedItems.includes(i)) this.selectedItems.push(i)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.lastClickedShift = item
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// perform a normal toggle
|
||||||
|
toggle()
|
||||||
|
},
|
||||||
|
onResize() {
|
||||||
const content = this.$refs.content as HTMLElement
|
const content = this.$refs.content as HTMLElement
|
||||||
this.width = computeCardWidth(content.clientWidth, this.$vuetify.breakpoint.name.toString())
|
this.width = computeCardWidth(content.clientWidth, this.$vuetify.breakpoint.name.toString())
|
||||||
},
|
},
|
||||||
deleteItem (item: any) {
|
deleteItem(item: any) {
|
||||||
const index = this.localItems.findIndex((e: any) => e.id === item.id)
|
const index = this.localItems.findIndex((e: any) => e.id === item.id)
|
||||||
this.localItems.splice(index, 1)
|
this.localItems.splice(index, 1)
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -266,9 +266,9 @@ export default Vue.extend({
|
||||||
this.thumbnailCacheBust = '?' + this.$_.random(1000)
|
this.thumbnailCacheBust = '?' + this.$_.random(1000)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onClick() {
|
onClick(e: MouseEvent) {
|
||||||
if (this.preselect && this.onSelected !== undefined) {
|
if (this.preselect && this.onSelected !== undefined) {
|
||||||
this.selectItem()
|
this.selectItem(e)
|
||||||
} else if (!this.noLink) {
|
} else if (!this.noLink) {
|
||||||
this.goto()
|
this.goto()
|
||||||
}
|
}
|
||||||
|
|
@ -276,9 +276,9 @@ export default Vue.extend({
|
||||||
goto() {
|
goto() {
|
||||||
this.$router.push(this.computedItem.to())
|
this.$router.push(this.computedItem.to())
|
||||||
},
|
},
|
||||||
selectItem() {
|
selectItem(e: MouseEvent) {
|
||||||
if (this.onSelected !== undefined) {
|
if (this.onSelected !== undefined) {
|
||||||
this.onSelected()
|
this.onSelected(this.item, e)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
editItem() {
|
editItem() {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue