mirror of
https://github.com/gotson/komga.git
synced 2025-01-07 03:07:16 +08:00
parent
b69c4f62a2
commit
1c28a9496b
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
* text=auto
|
@ -1,204 +1,204 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-item-group multiple v-model="selectedItems">
|
<v-item-group multiple v-model="selectedItems">
|
||||||
<div v-if="hasItems"
|
<div v-if="hasItems"
|
||||||
ref="content"
|
ref="content"
|
||||||
v-resize="onResize"
|
v-resize="onResize"
|
||||||
>
|
>
|
||||||
<draggable v-model="localItems"
|
<draggable v-model="localItems"
|
||||||
:class="flexClass"
|
:class="flexClass"
|
||||||
handle=".handle"
|
handle=".handle"
|
||||||
v-bind="dragOptions"
|
v-bind="dragOptions"
|
||||||
>
|
>
|
||||||
<transition-group type="transition"
|
<transition-group type="transition"
|
||||||
:name="!draggable ? 'flip-list' : null"
|
:name="!draggable ? 'flip-list' : null"
|
||||||
: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 ? toggle: undefined"
|
||||||
:action-menu="actionMenu"
|
:action-menu="actionMenu"
|
||||||
></item-card>
|
></item-card>
|
||||||
|
|
||||||
<v-slide-y-reverse-transition>
|
<v-slide-y-reverse-transition>
|
||||||
<v-icon v-if="draggable"
|
<v-icon v-if="draggable"
|
||||||
class="handle"
|
class="handle"
|
||||||
style="position: absolute; bottom: 0; left: 50%; margin-left: -12px;"
|
style="position: absolute; bottom: 0; left: 50%; margin-left: -12px;"
|
||||||
>
|
>
|
||||||
mdi-drag-horizontal
|
mdi-drag-horizontal
|
||||||
</v-icon>
|
</v-icon>
|
||||||
</v-slide-y-reverse-transition>
|
</v-slide-y-reverse-transition>
|
||||||
|
|
||||||
<!-- FAB delete (center) -->
|
<!-- FAB delete (center) -->
|
||||||
<v-fab-transition>
|
<v-fab-transition>
|
||||||
<v-btn
|
<v-btn
|
||||||
v-if="deletable"
|
v-if="deletable"
|
||||||
fab
|
fab
|
||||||
small
|
small
|
||||||
color="accent"
|
color="accent"
|
||||||
class="fab-delete"
|
class="fab-delete"
|
||||||
@click="deleteItem(item)"
|
@click="deleteItem(item)"
|
||||||
style="position: absolute; bottom: 10px; right: 10px;"
|
style="position: absolute; bottom: 10px; right: 10px;"
|
||||||
>
|
>
|
||||||
<v-icon>mdi-delete</v-icon>
|
<v-icon>mdi-delete</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-fab-transition>
|
</v-fab-transition>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</slot>
|
</slot>
|
||||||
</v-item>
|
</v-item>
|
||||||
</transition-group>
|
</transition-group>
|
||||||
</draggable>
|
</draggable>
|
||||||
</div>
|
</div>
|
||||||
<v-row v-else justify="center">
|
<v-row v-else justify="center">
|
||||||
<slot name="empty"></slot>
|
<slot name="empty"></slot>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-item-group>
|
</v-item-group>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<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,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
fixedItemWidth: {
|
fixedItemWidth: {
|
||||||
type: Number,
|
type: Number,
|
||||||
required: false,
|
required: false,
|
||||||
},
|
},
|
||||||
selectable: {
|
selectable: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
selected: {
|
selected: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
editFunction: {
|
editFunction: {
|
||||||
type: Function,
|
type: Function,
|
||||||
},
|
},
|
||||||
resizeFunction: {
|
resizeFunction: {
|
||||||
type: Function,
|
type: Function,
|
||||||
},
|
},
|
||||||
draggable: {
|
draggable: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
deletable: {
|
deletable: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
nowrap: {
|
nowrap: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
actionMenu: {
|
actionMenu: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data: () => {
|
data: () => {
|
||||||
return {
|
return {
|
||||||
selectedItems: [],
|
selectedItems: [],
|
||||||
localItems: [],
|
localItems: [],
|
||||||
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',
|
||||||
disabled: !this.draggable,
|
disabled: !this.draggable,
|
||||||
ghostClass: 'ghost',
|
ghostClass: 'ghost',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onResize () {
|
onResize () {
|
||||||
const content = this.$refs.content as HTMLElement
|
const content = this.$refs.content as HTMLElement
|
||||||
this.width = computeCardWidth(content.clientWidth, this.$vuetify.breakpoint.name)
|
this.width = computeCardWidth(content.clientWidth, this.$vuetify.breakpoint.name)
|
||||||
},
|
},
|
||||||
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)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.ghost .item-card {
|
.ghost .item-card {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
background: #c8ebfb;
|
background: #c8ebfb;
|
||||||
}
|
}
|
||||||
|
|
||||||
.handle {
|
.handle {
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flip-list-move {
|
.flip-list-move {
|
||||||
transition: transform 0.4s;
|
transition: transform 0.4s;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,297 +1,297 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-hover :disabled="disableHover">
|
<v-hover :disabled="disableHover">
|
||||||
<template v-slot:default="{ hover }">
|
<template v-slot:default="{ hover }">
|
||||||
<v-card
|
<v-card
|
||||||
:width="width"
|
:width="width"
|
||||||
@click="onClick"
|
@click="onClick"
|
||||||
:class="noLink ? 'no-link' : ''"
|
:class="noLink ? 'no-link' : ''"
|
||||||
:ripple="false"
|
:ripple="false"
|
||||||
>
|
>
|
||||||
<!-- Thumbnail-->
|
<!-- Thumbnail-->
|
||||||
<v-img
|
<v-img
|
||||||
:src="thumbnailUrl"
|
:src="thumbnailUrl"
|
||||||
lazy-src="../assets/cover.svg"
|
lazy-src="../assets/cover.svg"
|
||||||
aspect-ratio="0.7071"
|
aspect-ratio="0.7071"
|
||||||
contain
|
contain
|
||||||
>
|
>
|
||||||
<!-- unread tick for book -->
|
<!-- unread tick for book -->
|
||||||
<div class="unread" v-if="isUnread"/>
|
<div class="unread" v-if="isUnread"/>
|
||||||
|
|
||||||
<!-- unread count for series -->
|
<!-- unread count for series -->
|
||||||
<span v-if="unreadCount"
|
<span v-if="unreadCount"
|
||||||
class="white--text pa-1 px-2 subtitle-2"
|
class="white--text pa-1 px-2 subtitle-2"
|
||||||
:style="{background: 'orange', position: 'absolute', right: 0}"
|
:style="{background: 'orange', position: 'absolute', right: 0}"
|
||||||
>
|
>
|
||||||
{{ unreadCount }}
|
{{ unreadCount }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<!-- Thumbnail overlay -->
|
<!-- Thumbnail overlay -->
|
||||||
<v-fade-transition>
|
<v-fade-transition>
|
||||||
<v-overlay
|
<v-overlay
|
||||||
v-if="hover || selected || preselect || actionMenuState"
|
v-if="hover || selected || preselect || actionMenuState"
|
||||||
absolute
|
absolute
|
||||||
:opacity="hover || actionMenuState ? 0.3 : 0"
|
:opacity="hover || actionMenuState ? 0.3 : 0"
|
||||||
:class="`${hover || actionMenuState ? 'item-border-darken' : selected ? 'item-border' : 'item-border-transparent'} overlay-full`"
|
:class="`${hover || actionMenuState ? 'item-border-darken' : selected ? 'item-border' : 'item-border-transparent'} overlay-full`"
|
||||||
>
|
>
|
||||||
<!-- Circle icon for selection (top left) -->
|
<!-- Circle icon for selection (top left) -->
|
||||||
<v-icon v-if="onSelected"
|
<v-icon v-if="onSelected"
|
||||||
:color="selected ? 'secondary' : ''"
|
:color="selected ? 'secondary' : ''"
|
||||||
style="position: absolute; top: 5px; left: 10px"
|
style="position: absolute; top: 5px; left: 10px"
|
||||||
@click.stop="selectItem"
|
@click.stop="selectItem"
|
||||||
>
|
>
|
||||||
{{ selected || (preselect && hover) ? 'mdi-checkbox-marked-circle' : 'mdi-checkbox-blank-circle-outline'
|
{{ selected || (preselect && hover) ? 'mdi-checkbox-marked-circle' : 'mdi-checkbox-blank-circle-outline'
|
||||||
}}
|
}}
|
||||||
</v-icon>
|
</v-icon>
|
||||||
|
|
||||||
<!-- FAB reading (center) -->
|
<!-- FAB reading (center) -->
|
||||||
<v-btn
|
<v-btn
|
||||||
v-if="bookReady && !selected && !preselect && canReadPages"
|
v-if="bookReady && !selected && !preselect && canReadPages"
|
||||||
fab
|
fab
|
||||||
x-large
|
x-large
|
||||||
color="accent"
|
color="accent"
|
||||||
style="position: absolute; top: 50%; left: 50%; margin-left: -36px; margin-top: -36px"
|
style="position: absolute; top: 50%; left: 50%; margin-left: -36px; margin-top: -36px"
|
||||||
:to="{name: 'read-book', params: { bookId: item.id}}"
|
:to="{name: 'read-book', params: { bookId: item.id}}"
|
||||||
>
|
>
|
||||||
<v-icon>mdi-book-open-page-variant</v-icon>
|
<v-icon>mdi-book-open-page-variant</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
|
||||||
<!-- Pen icon for edition (bottom left) -->
|
<!-- Pen icon for edition (bottom left) -->
|
||||||
<v-btn icon
|
<v-btn icon
|
||||||
v-if="!selected && !preselect && onEdit"
|
v-if="!selected && !preselect && onEdit"
|
||||||
style="position: absolute; bottom: 5px; left: 5px"
|
style="position: absolute; bottom: 5px; left: 5px"
|
||||||
@click.stop="editItem"
|
@click.stop="editItem"
|
||||||
>
|
>
|
||||||
<v-icon>mdi-pencil</v-icon>
|
<v-icon>mdi-pencil</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
|
||||||
<!-- Action menu (bottom right) -->
|
<!-- Action menu (bottom right) -->
|
||||||
<div v-if="!selected && !preselect && actionMenu"
|
<div v-if="!selected && !preselect && actionMenu"
|
||||||
style="position: absolute; bottom: 5px; right: 5px"
|
style="position: absolute; bottom: 5px; right: 5px"
|
||||||
>
|
>
|
||||||
<book-actions-menu v-if="computedItem.type() === ItemTypes.BOOK"
|
<book-actions-menu v-if="computedItem.type() === ItemTypes.BOOK"
|
||||||
:book="item"
|
:book="item"
|
||||||
:menu.sync="actionMenuState"
|
:menu.sync="actionMenuState"
|
||||||
/>
|
/>
|
||||||
<series-actions-menu v-if="computedItem.type() === ItemTypes.SERIES"
|
<series-actions-menu v-if="computedItem.type() === ItemTypes.SERIES"
|
||||||
:series="item"
|
:series="item"
|
||||||
:menu.sync="actionMenuState"
|
:menu.sync="actionMenuState"
|
||||||
/>
|
/>
|
||||||
<collection-actions-menu v-if="computedItem.type() === ItemTypes.COLLECTION"
|
<collection-actions-menu v-if="computedItem.type() === ItemTypes.COLLECTION"
|
||||||
:collection="item"
|
:collection="item"
|
||||||
:menu.sync="actionMenuState"
|
:menu.sync="actionMenuState"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</v-overlay>
|
</v-overlay>
|
||||||
</v-fade-transition>
|
</v-fade-transition>
|
||||||
<v-progress-linear
|
<v-progress-linear
|
||||||
v-if="isInProgress"
|
v-if="isInProgress"
|
||||||
:value="readProgressPercentage"
|
:value="readProgressPercentage"
|
||||||
color="orange"
|
color="orange"
|
||||||
height="6"
|
height="6"
|
||||||
style="position: absolute; bottom: 0"
|
style="position: absolute; bottom: 0"
|
||||||
/>
|
/>
|
||||||
</v-img>
|
</v-img>
|
||||||
|
|
||||||
<!-- Description-->
|
<!-- Description-->
|
||||||
<template v-if="!thumbnailOnly">
|
<template v-if="!thumbnailOnly">
|
||||||
<router-link :to="to" class="link-underline">
|
<router-link :to="to" class="link-underline">
|
||||||
<v-card-subtitle
|
<v-card-subtitle
|
||||||
v-line-clamp="2"
|
v-line-clamp="2"
|
||||||
v-bind="subtitleProps"
|
v-bind="subtitleProps"
|
||||||
v-html="title"
|
v-html="title"
|
||||||
>
|
>
|
||||||
</v-card-subtitle>
|
</v-card-subtitle>
|
||||||
</router-link>
|
</router-link>
|
||||||
<v-card-text class="px-2" v-html="body">
|
<v-card-text class="px-2" v-html="body">
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
</template>
|
</template>
|
||||||
</v-card>
|
</v-card>
|
||||||
</template>
|
</template>
|
||||||
</v-hover>
|
</v-hover>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import BookActionsMenu from '@/components/menus/BookActionsMenu.vue'
|
import BookActionsMenu from '@/components/menus/BookActionsMenu.vue'
|
||||||
import CollectionActionsMenu from '@/components/menus/CollectionActionsMenu.vue'
|
import CollectionActionsMenu from '@/components/menus/CollectionActionsMenu.vue'
|
||||||
import SeriesActionsMenu from '@/components/menus/SeriesActionsMenu.vue'
|
import SeriesActionsMenu from '@/components/menus/SeriesActionsMenu.vue'
|
||||||
import { getReadProgress, getReadProgressPercentage } from '@/functions/book-progress'
|
import { getReadProgress, getReadProgressPercentage } from '@/functions/book-progress'
|
||||||
import { ReadStatus } from '@/types/enum-books'
|
import { ReadStatus } from '@/types/enum-books'
|
||||||
import { createItem, Item, ItemTypes } from '@/types/items'
|
import { createItem, Item, ItemTypes } from '@/types/items'
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import { RawLocation } from 'vue-router'
|
import { RawLocation } from 'vue-router'
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
name: 'ItemCard',
|
name: 'ItemCard',
|
||||||
components: { BookActionsMenu, SeriesActionsMenu, CollectionActionsMenu },
|
components: { BookActionsMenu, SeriesActionsMenu, CollectionActionsMenu },
|
||||||
props: {
|
props: {
|
||||||
item: {
|
item: {
|
||||||
type: Object as () => BookDto | SeriesDto | CollectionDto,
|
type: Object as () => BookDto | SeriesDto | CollectionDto,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
// hide the bottom part of the card
|
// hide the bottom part of the card
|
||||||
thumbnailOnly: {
|
thumbnailOnly: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
// disables the default link on clicking the card
|
// disables the default link on clicking the card
|
||||||
noLink: {
|
noLink: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
width: {
|
width: {
|
||||||
type: [String, Number],
|
type: [String, Number],
|
||||||
required: false,
|
required: false,
|
||||||
default: 150,
|
default: 150,
|
||||||
},
|
},
|
||||||
// when true, card will show the active border and circle icon full
|
// when true, card will show the active border and circle icon full
|
||||||
selected: {
|
selected: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
// when true, will display the border like if the card was hovered, and click anywhere will trigger onSelected
|
// when true, will display the border like if the card was hovered, and click anywhere will trigger onSelected
|
||||||
preselect: {
|
preselect: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
},
|
},
|
||||||
// callback function to call when selecting the card
|
// callback function to call when selecting the card
|
||||||
onSelected: {
|
onSelected: {
|
||||||
type: Function,
|
type: Function,
|
||||||
default: undefined,
|
default: undefined,
|
||||||
required: false,
|
required: false,
|
||||||
},
|
},
|
||||||
// callback function for the edit button
|
// callback function for the edit button
|
||||||
onEdit: {
|
onEdit: {
|
||||||
type: Function,
|
type: Function,
|
||||||
default: undefined,
|
default: undefined,
|
||||||
required: false,
|
required: false,
|
||||||
},
|
},
|
||||||
// action menu enabled or not
|
// action menu enabled or not
|
||||||
actionMenu: {
|
actionMenu: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data: () => {
|
data: () => {
|
||||||
return {
|
return {
|
||||||
ItemTypes,
|
ItemTypes,
|
||||||
actionMenuState: false,
|
actionMenuState: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
canReadPages (): boolean {
|
canReadPages (): boolean {
|
||||||
return this.$store.getters.mePageStreaming && this.computedItem.type() === ItemTypes.BOOK
|
return this.$store.getters.mePageStreaming && this.computedItem.type() === ItemTypes.BOOK
|
||||||
},
|
},
|
||||||
overlay (): boolean {
|
overlay (): boolean {
|
||||||
return this.onEdit !== undefined || this.onSelected !== undefined || this.bookReady || this.canReadPages || this.actionMenu
|
return this.onEdit !== undefined || this.onSelected !== undefined || this.bookReady || this.canReadPages || this.actionMenu
|
||||||
},
|
},
|
||||||
computedItem (): Item<BookDto | SeriesDto | CollectionDto> {
|
computedItem (): Item<BookDto | SeriesDto | CollectionDto> {
|
||||||
return createItem(this.item)
|
return createItem(this.item)
|
||||||
},
|
},
|
||||||
disableHover (): boolean {
|
disableHover (): boolean {
|
||||||
return !this.overlay
|
return !this.overlay
|
||||||
},
|
},
|
||||||
thumbnailUrl (): string {
|
thumbnailUrl (): string {
|
||||||
return this.computedItem.thumbnailUrl()
|
return this.computedItem.thumbnailUrl()
|
||||||
},
|
},
|
||||||
title (): string {
|
title (): string {
|
||||||
return this.computedItem.title()
|
return this.computedItem.title()
|
||||||
},
|
},
|
||||||
subtitleProps (): Object {
|
subtitleProps (): Object {
|
||||||
return this.computedItem.subtitleProps()
|
return this.computedItem.subtitleProps()
|
||||||
},
|
},
|
||||||
body (): string {
|
body (): string {
|
||||||
return this.computedItem.body()
|
return this.computedItem.body()
|
||||||
},
|
},
|
||||||
isInProgress (): boolean {
|
isInProgress (): boolean {
|
||||||
if (this.computedItem.type() === ItemTypes.BOOK) return getReadProgress(this.item as BookDto) === ReadStatus.IN_PROGRESS
|
if (this.computedItem.type() === ItemTypes.BOOK) return getReadProgress(this.item as BookDto) === ReadStatus.IN_PROGRESS
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
isUnread (): boolean {
|
isUnread (): boolean {
|
||||||
if (this.computedItem.type() === ItemTypes.BOOK) return getReadProgress(this.item as BookDto) === ReadStatus.UNREAD
|
if (this.computedItem.type() === ItemTypes.BOOK) return getReadProgress(this.item as BookDto) === ReadStatus.UNREAD
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
unreadCount (): number | undefined {
|
unreadCount (): number | undefined {
|
||||||
if (this.computedItem.type() === ItemTypes.SERIES) return (this.item as SeriesDto).booksUnreadCount
|
if (this.computedItem.type() === ItemTypes.SERIES) return (this.item as SeriesDto).booksUnreadCount
|
||||||
return undefined
|
return undefined
|
||||||
},
|
},
|
||||||
readProgressPercentage (): number {
|
readProgressPercentage (): number {
|
||||||
if (this.computedItem.type() === ItemTypes.BOOK) return getReadProgressPercentage(this.item as BookDto)
|
if (this.computedItem.type() === ItemTypes.BOOK) return getReadProgressPercentage(this.item as BookDto)
|
||||||
return 0
|
return 0
|
||||||
},
|
},
|
||||||
bookReady (): boolean {
|
bookReady (): boolean {
|
||||||
if (this.computedItem.type() === ItemTypes.BOOK) {
|
if (this.computedItem.type() === ItemTypes.BOOK) {
|
||||||
return (this.item as BookDto).media.status === 'READY'
|
return (this.item as BookDto).media.status === 'READY'
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
to (): RawLocation {
|
to (): RawLocation {
|
||||||
return this.computedItem.to()
|
return this.computedItem.to()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onClick () {
|
onClick () {
|
||||||
if (this.preselect && this.onSelected !== undefined) {
|
if (this.preselect && this.onSelected !== undefined) {
|
||||||
this.selectItem()
|
this.selectItem()
|
||||||
} else if (!this.noLink) {
|
} else if (!this.noLink) {
|
||||||
this.goto()
|
this.goto()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
goto () {
|
goto () {
|
||||||
this.$router.push(this.computedItem.to())
|
this.$router.push(this.computedItem.to())
|
||||||
},
|
},
|
||||||
selectItem () {
|
selectItem () {
|
||||||
if (this.onSelected !== undefined) {
|
if (this.onSelected !== undefined) {
|
||||||
this.onSelected()
|
this.onSelected()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
editItem () {
|
editItem () {
|
||||||
if (this.onEdit !== undefined) {
|
if (this.onEdit !== undefined) {
|
||||||
this.onEdit(this.item)
|
this.onEdit(this.item)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.no-link {
|
.no-link {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-border {
|
.item-border {
|
||||||
border: 3px solid var(--v-secondary-base);
|
border: 3px solid var(--v-secondary-base);
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-border-transparent {
|
.item-border-transparent {
|
||||||
border: 3px solid transparent;
|
border: 3px solid transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-border-darken {
|
.item-border-darken {
|
||||||
border: 3px solid var(--v-secondary-darken2);
|
border: 3px solid var(--v-secondary-darken2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-full .v-overlay__content {
|
.overlay-full .v-overlay__content {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.unread {
|
.unread {
|
||||||
border-left: 25px solid transparent;
|
border-left: 25px solid transparent;
|
||||||
border-right: 25px solid orange;
|
border-right: 25px solid orange;
|
||||||
border-bottom: 25px solid transparent;
|
border-bottom: 25px solid transparent;
|
||||||
height: 0;
|
height: 0;
|
||||||
width: 0;
|
width: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.link-underline {
|
.link-underline {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.link-underline:hover {
|
.link-underline:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
text-decoration-color: black;
|
text-decoration-color: black;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
Loading…
Reference in New Issue
Block a user