refactor(webui): carve out multi-select bars

This commit is contained in:
Gauthier Roebroeck 2020-06-28 20:20:16 +08:00
parent bc45d0c3c1
commit a1cb186a28
10 changed files with 210 additions and 182 deletions

View file

@ -17,7 +17,7 @@
v-for="item in localItems"
:key="item.id"
class="my-3 mx-2"
v-slot:default="{ toggle, active }" :value="$_.get(item, 'id', 0)"
v-slot:default="{ toggle, active }" :value="item"
>
<slot name="item">
<div style="position: relative"

View file

@ -0,0 +1,79 @@
<template>
<v-scroll-y-transition hide-on-leave>
<toolbar-sticky v-if="value.length > 0" :elevation="5" color="white">
<v-btn icon @click="unselectAll">
<v-icon>mdi-close</v-icon>
</v-btn>
<v-toolbar-title>
<span>{{ value.length }} selected</span>
</v-toolbar-title>
<v-spacer/>
<v-btn icon @click="markRead">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-icon v-on="on">mdi-bookmark-check</v-icon>
</template>
<span>Mark as Read</span>
</v-tooltip>
</v-btn>
<v-btn icon @click="markUnread">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-icon v-on="on">mdi-bookmark-remove</v-icon>
</template>
<span>Mark as Unread</span>
</v-tooltip>
</v-btn>
<v-btn icon @click="edit" v-if="isAdmin">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-icon v-on="on">mdi-pencil</v-icon>
</template>
<span>Edit metadata</span>
</v-tooltip>
</v-btn>
</toolbar-sticky>
</v-scroll-y-transition>
</template>
<script lang="ts">
import Vue from 'vue'
import ToolbarSticky from './ToolbarSticky.vue'
export default Vue.extend({
name: 'BooksMultiSelectBar',
components: { ToolbarSticky },
data: () => {
return {}
},
props: {
value: {
type: Array,
required: true,
},
},
computed: {
isAdmin (): boolean {
return this.$store.getters.meAdmin
},
},
methods: {
unselectAll () {
this.$emit('unselect-all')
},
markRead () {
this.$emit('mark-read')
},
markUnread () {
this.$emit('mark-unread')
},
edit () {
this.$emit('edit')
},
},
})
</script>

View file

@ -0,0 +1,86 @@
<template>
<v-scroll-y-transition hide-on-leave>
<toolbar-sticky v-if="value.length > 0" :elevation="5" color="white">
<v-btn icon @click="unselectAll">
<v-icon>mdi-close</v-icon>
</v-btn>
<v-toolbar-title>
<span>{{ value.length }} selected</span>
</v-toolbar-title>
<v-spacer/>
<v-btn icon @click="markRead">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-icon v-on="on">mdi-bookmark-check</v-icon>
</template>
<span>Mark as Read</span>
</v-tooltip>
</v-btn>
<v-btn icon @click="markUnread">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-icon v-on="on">mdi-bookmark-remove</v-icon>
</template>
<span>Mark as Unread</span>
</v-tooltip>
</v-btn>
<v-btn icon @click="addToCollection" v-if="isAdmin">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-icon v-on="on">mdi-playlist-plus</v-icon>
</template>
<span>Add to collection</span>
</v-tooltip>
</v-btn>
<v-btn icon @click="edit" v-if="isAdmin">
<v-icon>mdi-pencil</v-icon>
</v-btn>
</toolbar-sticky>
</v-scroll-y-transition>
</template>
<script lang="ts">
import Vue from 'vue'
import ToolbarSticky from './ToolbarSticky.vue'
export default Vue.extend({
name: 'SeriesMultiSelectBar',
components: { ToolbarSticky },
data: () => {
return {}
},
props: {
value: {
type: Array,
required: true,
},
},
computed: {
isAdmin (): boolean {
return this.$store.getters.meAdmin
},
},
methods: {
unselectAll () {
this.$emit('unselect-all')
},
markRead () {
this.$emit('mark-read')
},
markUnread () {
this.$emit('mark-unread')
},
addToCollection () {
this.$emit('add-to-collection')
},
edit () {
this.$emit('edit')
},
},
})
</script>

View file

@ -146,7 +146,7 @@
import Badge from '@/components/Badge.vue'
import BookActionsMenu from '@/components/menus/BookActionsMenu.vue'
import ItemCard from '@/components/ItemCard.vue'
import ToolbarSticky from '@/components/ToolbarSticky.vue'
import ToolbarSticky from '@/components/bars/ToolbarSticky.vue'
import { groupAuthorsByRolePlural } from '@/functions/authors'
import { getBookFormatFromMediaType } from '@/functions/book-format'
import { getReadProgress, getReadProgressPercentage } from '@/functions/book-progress'

View file

@ -1,6 +1,6 @@
<template>
<div v-if="collection">
<toolbar-sticky v-if="!editElements && selected.length === 0">
<toolbar-sticky v-if="!editElements && selectedSeries.length === 0">
<collection-actions-menu v-if="collection"
:collection="collection"
@ -36,6 +36,15 @@
</toolbar-sticky>
<series-multi-select-bar
v-model="selectedSeries"
@unselect-all="selectedSeries = []"
@mark-read="markSelectedRead"
@mark-unread="markSelectedUnread"
@add-to-collection="addToCollection"
@edit="editMultipleSeries"
/>
<!-- Edit elements sticky bar -->
<v-scroll-y-transition hide-on-leave>
<toolbar-sticky v-if="editElements" :elevation="5" color="white">
@ -50,61 +59,11 @@
</toolbar-sticky>
</v-scroll-y-transition>
<!-- Selection sticky bar -->
<v-scroll-y-transition hide-on-leave>
<toolbar-sticky v-if="selected.length > 0" :elevation="5" color="white">
<v-btn icon @click="selected=[]">
<v-icon>mdi-close</v-icon>
</v-btn>
<v-toolbar-title>
<span>{{ selected.length }} selected</span>
</v-toolbar-title>
<v-spacer/>
<v-btn icon @click="markSelectedRead">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-icon v-on="on">mdi-bookmark-check</v-icon>
</template>
<span>Mark as Read</span>
</v-tooltip>
</v-btn>
<v-btn icon @click="markSelectedUnread">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-icon v-on="on">mdi-bookmark-remove</v-icon>
</template>
<span>Mark as Unread</span>
</v-tooltip>
</v-btn>
<v-btn icon @click="addToCollection">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-icon v-on="on">mdi-playlist-plus</v-icon>
</template>
<span>Add to collection</span>
</v-tooltip>
</v-btn>
<v-btn icon @click="editMultipleSeries" v-if="isAdmin">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-icon v-on="on">mdi-pencil</v-icon>
</template>
<span>Edit metadata</span>
</v-tooltip>
</v-btn>
</toolbar-sticky>
</v-scroll-y-transition>
<v-container fluid>
<item-browser
:items.sync="series"
:selected.sync="selected"
:selected.sync="selectedSeries"
:edit-function="editSingleSeries"
:draggable="editElements && collection.ordered"
:deletable="editElements"
@ -119,9 +78,10 @@
import Badge from '@/components/Badge.vue'
import CollectionActionsMenu from '@/components/menus/CollectionActionsMenu.vue'
import ItemBrowser from '@/components/ItemBrowser.vue'
import ToolbarSticky from '@/components/ToolbarSticky.vue'
import ToolbarSticky from '@/components/bars/ToolbarSticky.vue'
import { COLLECTION_CHANGED, COLLECTION_DELETED, SERIES_CHANGED } from '@/types/events'
import Vue from 'vue'
import SeriesMultiSelectBar from '@/components/bars/SeriesMultiSelectBar.vue'
export default Vue.extend({
name: 'BrowseCollection',
@ -130,6 +90,7 @@ export default Vue.extend({
ItemBrowser,
CollectionActionsMenu,
Badge,
SeriesMultiSelectBar,
},
data: () => {
return {
@ -137,8 +98,6 @@ export default Vue.extend({
series: [] as SeriesDto[],
seriesCopy: [] as SeriesDto[],
selectedSeries: [] as SeriesDto[],
editSeriesSingle: {} as SeriesDto,
selected: [],
editElements: false,
}
},
@ -149,10 +108,6 @@ export default Vue.extend({
},
},
watch: {
selected (val: number[]) {
this.selectedSeries = val.map(id => this.series.find(s => s.id === id))
.filter(x => x !== undefined) as SeriesDto[]
},
selectedSeries (val: SeriesDto[]) {
val.forEach(s => {
let index = this.series.findIndex(x => x.id === s.id)
@ -165,16 +120,6 @@ export default Vue.extend({
}
})
},
editSeriesSingle (val: SeriesDto) {
let index = this.series.findIndex(x => x.id === val.id)
if (index !== -1) {
this.series.splice(index, 1, val)
}
index = this.seriesCopy.findIndex(x => x.id === val.id)
if (index !== -1) {
this.seriesCopy.splice(index, 1, val)
}
},
},
created () {
this.$eventHub.$on(COLLECTION_CHANGED, this.collectionChanged)

View file

@ -42,7 +42,7 @@ import ItemBrowser from '@/components/ItemBrowser.vue'
import LibraryActionsMenu from '@/components/menus/LibraryActionsMenu.vue'
import LibraryNavigation from '@/components/LibraryNavigation.vue'
import PageSizeSelect from '@/components/PageSizeSelect.vue'
import ToolbarSticky from '@/components/ToolbarSticky.vue'
import ToolbarSticky from '@/components/bars/ToolbarSticky.vue'
import { COLLECTION_CHANGED } from '@/types/events'
import Vue from 'vue'

View file

@ -1,6 +1,6 @@
<template>
<div :style="$vuetify.breakpoint.name === 'xs' ? 'margin-bottom: 56px' : undefined">
<toolbar-sticky v-if="selected.length === 0">
<toolbar-sticky v-if="selectedSeries.length === 0">
<!-- Action menu -->
<library-actions-menu v-if="library"
:library="library"/>
@ -24,58 +24,16 @@
/>
<page-size-select v-model="pageSize"/>
</toolbar-sticky>
<!-- Selection sticky bar -->
<v-scroll-y-transition hide-on-leave>
<toolbar-sticky v-if="selected.length > 0" :elevation="5" color="white">
<v-btn icon @click="selected=[]">
<v-icon>mdi-close</v-icon>
</v-btn>
<v-toolbar-title>
<span>{{ selected.length }} selected</span>
</v-toolbar-title>
<v-spacer/>
<v-btn icon @click="markSelectedRead">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-icon v-on="on">mdi-bookmark-check</v-icon>
</template>
<span>Mark as Read</span>
</v-tooltip>
</v-btn>
<v-btn icon @click="markSelectedUnread">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-icon v-on="on">mdi-bookmark-remove</v-icon>
</template>
<span>Mark as Unread</span>
</v-tooltip>
</v-btn>
<v-btn icon @click="addToCollection">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-icon v-on="on">mdi-playlist-plus</v-icon>
</template>
<span>Add to collection</span>
</v-tooltip>
</v-btn>
<v-btn icon @click="editMultipleSeries" v-if="isAdmin">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-icon v-on="on">mdi-pencil</v-icon>
</template>
<span>Edit metadata</span>
</v-tooltip>
</v-btn>
</toolbar-sticky>
</v-scroll-y-transition>
<series-multi-select-bar
v-model="selectedSeries"
@unselect-all="selectedSeries = []"
@mark-read="markSelectedRead"
@mark-unread="markSelectedUnread"
@add-to-collection="addToCollection"
@edit="editMultipleSeries"
/>
<library-navigation v-if="collectionsCount > 0"
:libraryId="libraryId"
@ -101,7 +59,7 @@
<item-browser
:items="series"
:selected.sync="selected"
:selected.sync="selectedSeries"
:edit-function="editSingleSeries"
/>
</template>
@ -119,12 +77,13 @@ import LibraryActionsMenu from '@/components/menus/LibraryActionsMenu.vue'
import LibraryNavigation from '@/components/LibraryNavigation.vue'
import PageSizeSelect from '@/components/PageSizeSelect.vue'
import SortMenuButton from '@/components/SortMenuButton.vue'
import ToolbarSticky from '@/components/ToolbarSticky.vue'
import ToolbarSticky from '@/components/bars/ToolbarSticky.vue'
import { parseQueryFilter, parseQuerySort } from '@/functions/query-params'
import { ReadStatus } from '@/types/enum-books'
import { SeriesStatus } from '@/types/enum-series'
import { COLLECTION_CHANGED, LIBRARY_DELETED, SERIES_CHANGED } from '@/types/events'
import Vue from 'vue'
import SeriesMultiSelectBar from '@/components/bars/SeriesMultiSelectBar.vue'
const cookiePageSize = 'pagesize'
@ -140,13 +99,13 @@ export default Vue.extend({
ItemBrowser,
PageSizeSelect,
LibraryNavigation,
SeriesMultiSelectBar,
},
data: () => {
return {
library: undefined as LibraryDto | undefined,
series: [] as SeriesDto[],
selectedSeries: [] as SeriesDto[],
editSeriesSingle: {} as SeriesDto,
page: 1,
pageSize: 20,
totalPages: 1,
@ -164,7 +123,6 @@ export default Vue.extend({
filterUnwatch: null as any,
pageUnwatch: null as any,
pageSizeUnwatch: null as any,
selected: [],
collectionsCount: 0,
}
},
@ -175,10 +133,6 @@ export default Vue.extend({
},
},
watch: {
selected (val: number[]) {
this.selectedSeries = val.map(id => this.series.find(s => s.id === id))
.filter(x => x !== undefined) as SeriesDto[]
},
selectedSeries (val: SeriesDto[]) {
val.forEach(s => {
const index = this.series.findIndex(x => x.id === s.id)
@ -187,12 +141,6 @@ export default Vue.extend({
}
})
},
editSeriesSingle (val: SeriesDto) {
const index = this.series.findIndex(x => x.id === val.id)
if (index !== -1) {
this.series.splice(index, 1, val)
}
},
},
created () {
this.$eventHub.$on(COLLECTION_CHANGED, this.reloadCollections)
@ -289,7 +237,7 @@ export default Vue.extend({
updateRouteAndReload () {
this.unsetWatches()
this.selected = []
this.selectedSeries = []
this.page = 1
this.updateRoute()

View file

@ -1,6 +1,6 @@
<template>
<div>
<toolbar-sticky v-if="selected.length === 0">
<toolbar-sticky v-if="selectedBooks.length === 0">
<!-- Go back to parent library -->
<v-btn icon
title="Go to library"
@ -39,40 +39,13 @@
</toolbar-sticky>
<v-scroll-y-transition hide-on-leave>
<toolbar-sticky v-if="selected.length > 0" :elevation="5" color="white">
<v-btn icon @click="selected=[]">
<v-icon>mdi-close</v-icon>
</v-btn>
<v-toolbar-title>
<span>{{ selected.length }} selected</span>
</v-toolbar-title>
<v-spacer/>
<v-btn icon @click="markSelectedRead()">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-icon v-on="on">mdi-bookmark-check</v-icon>
</template>
<span>Mark as Read</span>
</v-tooltip>
</v-btn>
<v-btn icon @click="markSelectedUnread()">
<v-tooltip bottom>
<template v-slot:activator="{ on }">
<v-icon v-on="on">mdi-bookmark-remove</v-icon>
</template>
<span>Mark as Unread</span>
</v-tooltip>
</v-btn>
<v-btn icon @click="editMultipleBooks" v-if="isAdmin">
<v-icon>mdi-pencil</v-icon>
</v-btn>
</toolbar-sticky>
</v-scroll-y-transition>
<books-multi-select-bar
v-model="selectedBooks"
@unselect-all="selectedBooks = []"
@mark-read="markSelectedRead"
@mark-unread="markSelectedUnread"
@edit="editMultipleBooks"
/>
<v-container fluid>
<v-row>
@ -179,7 +152,7 @@
/>
<item-browser :items="books"
:selected.sync="selected"
:selected.sync="selectedBooks"
:edit-function="editSingleBook"
/>
</template>
@ -199,12 +172,13 @@ import ItemCard from '@/components/ItemCard.vue'
import PageSizeSelect from '@/components/PageSizeSelect.vue'
import SeriesActionsMenu from '@/components/menus/SeriesActionsMenu.vue'
import SortMenuButton from '@/components/SortMenuButton.vue'
import ToolbarSticky from '@/components/ToolbarSticky.vue'
import ToolbarSticky from '@/components/bars/ToolbarSticky.vue'
import { parseQueryFilter, parseQuerySort } from '@/functions/query-params'
import { seriesThumbnailUrl } from '@/functions/urls'
import { ReadStatus } from '@/types/enum-books'
import { BOOK_CHANGED, LIBRARY_DELETED, SERIES_CHANGED } from '@/types/events'
import Vue from 'vue'
import BooksMultiSelectBar from '@/components/bars/BooksMultiSelectBar.vue'
const cookiePageSize = 'pagesize'
@ -221,6 +195,7 @@ export default Vue.extend({
HorizontalScroller,
ItemCard,
EmptyState,
BooksMultiSelectBar,
},
data: () => {
return {
@ -243,7 +218,6 @@ export default Vue.extend({
filterUnwatch: null as any,
pageUnwatch: null as any,
pageSizeUnwatch: null as any,
selected: [],
collections: [] as CollectionDto[],
collectionsContent: [] as any[][],
collectionPanel: -1,
@ -282,10 +256,6 @@ export default Vue.extend({
document.title = `Komga - ${val.metadata.title}`
}
},
selected (val: number[]) {
this.selectedBooks = val.map(id => this.books.find(s => s.id === id))
.filter(x => x !== undefined) as BookDto[]
},
},
created () {
this.$eventHub.$on(SERIES_CHANGED, this.reloadSeries)
@ -356,7 +326,7 @@ export default Vue.extend({
updateRouteAndReload () {
this.unsetWatches()
this.selected = []
this.selectedBooks = []
this.page = 1
this.updateRoute()

View file

@ -54,7 +54,7 @@
<script lang="ts">
import EmptyState from '@/components/EmptyState.vue'
import HorizontalScroller from '@/components/HorizontalScroller.vue'
import ToolbarSticky from '@/components/ToolbarSticky.vue'
import ToolbarSticky from '@/components/bars/ToolbarSticky.vue'
import { BOOK_CHANGED, COLLECTION_CHANGED, LIBRARY_DELETED, SERIES_CHANGED } from '@/types/events'
import Vue from 'vue'
import ItemBrowser from '@/components/ItemBrowser.vue'