151 lines
4.1 KiB
Vue
151 lines
4.1 KiB
Vue
<script setup>
|
|
import { computed, ref, useTemplateRef } from 'vue'
|
|
import { getCollection, postCollectionChangePosition, deleteCollectionItem } from './api.js'
|
|
import CollectionFormElement from './CollectionFormElement.vue'
|
|
import AddBookToCollection from './AddBookToCollection.vue'
|
|
import Pagination from './Pagination.vue'
|
|
|
|
const props = defineProps({
|
|
id: String,
|
|
})
|
|
|
|
const limit = 5
|
|
const pageNumber = ref(1)
|
|
|
|
const offset = computed(() => (pageNumber.value - 1) * limit)
|
|
|
|
const data = ref(null)
|
|
const error = ref(null)
|
|
|
|
const itemRefs = useTemplateRef('items')
|
|
|
|
const itemIdBeingGrabbed = ref(null)
|
|
|
|
const itemIdBeingOvered = ref(null)
|
|
|
|
let totalElementsNumber = computed(() =>
|
|
typeof data != 'undefined' && data.value != null ? data.value['count'] : 0,
|
|
)
|
|
|
|
let pageTotal = computed(() => Math.ceil(totalElementsNumber.value / limit))
|
|
|
|
getCollection(data, error, props.id, limit, offset.value)
|
|
|
|
function pageChange(newPageNumber) {
|
|
pageNumber.value = newPageNumber
|
|
data.value = null
|
|
error.value = null
|
|
getCollection(data, error, props.id, limit, offset.value)
|
|
}
|
|
|
|
function fetchCollection() {
|
|
pageChange(1)
|
|
}
|
|
|
|
function checkGrabbedPosition(itemId, y) {
|
|
const itemBeingMoved = itemRefs.value.find((it) => it.id == itemId)
|
|
const itemMovedY = itemBeingMoved.$el.offsetTop + y + itemBeingMoved.$el.offsetHeight / 2
|
|
itemRefs.value.forEach((it) => {
|
|
if (it.$props.id == itemIdBeingGrabbed.value) {
|
|
return
|
|
}
|
|
let lowerDetectionY = it.$el.offsetTop + it.$el.offsetHeight / 2
|
|
let upperDetectionY = it.$el.offsetTop + (3 * it.$el.offsetHeight) / 2
|
|
if (it.$props.position < itemBeingMoved.$props.position) {
|
|
lowerDetectionY = it.$el.offsetTop
|
|
upperDetectionY = it.$el.offsetTop + it.$el.offsetHeight / 2
|
|
}
|
|
if (lowerDetectionY < itemMovedY && itemMovedY < upperDetectionY) {
|
|
itemIdBeingOvered.value = it.$props.id
|
|
}
|
|
})
|
|
}
|
|
|
|
function onStopGrab() {
|
|
if (itemIdBeingOvered.value != null) {
|
|
const position = data.value.items.find((it) => it.id == itemIdBeingOvered.value).position
|
|
changePosition(itemIdBeingGrabbed.value, position)
|
|
}
|
|
itemIdBeingGrabbed.value = null
|
|
itemIdBeingOvered.value = null
|
|
}
|
|
|
|
function isDragoverFromAbove(position) {
|
|
if (itemIdBeingGrabbed.value == null) {
|
|
return false
|
|
}
|
|
const grabbedItemPosition = data.value.items.find(
|
|
(it) => it.id == itemIdBeingGrabbed.value,
|
|
).position
|
|
return position > grabbedItemPosition
|
|
}
|
|
|
|
function changePosition(id, position) {
|
|
postCollectionChangePosition(props.id, id, position).then((res) => {
|
|
if (res.ok) {
|
|
getCollection(data, error, props.id, limit, offset.value)
|
|
} else {
|
|
res.json().then((json) => {
|
|
error.value = json
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
function deleteItem(id) {
|
|
deleteCollectionItem(id).then((res) => {
|
|
if (res.ok) {
|
|
getCollection(data, error, props.id, limit, offset.value)
|
|
} else {
|
|
res.json().then((json) => {
|
|
error.value = json
|
|
})
|
|
}
|
|
})
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div v-if="error">{{ $t('collection.error', { error: error }) }}</div>
|
|
<div v-if="data">
|
|
<h2 class="title">{{ data.name }}</h2>
|
|
<AddBookToCollection :collection-id="props.id" @created="fetchCollection" />
|
|
<TransitionGroup name="list" tag="div">
|
|
<CollectionFormElement
|
|
@positionchange="(pos) => changePosition(item.id, pos)"
|
|
@startgrab="itemIdBeingGrabbed = item.id"
|
|
@grabbing="(y) => checkGrabbedPosition(item.id, y)"
|
|
@stopgrab="onStopGrab"
|
|
@delete="deleteItem(item.id)"
|
|
v-for="item in data.items"
|
|
:key="item.id"
|
|
v-bind="item"
|
|
ref="items"
|
|
:is-dragover="item.id == itemIdBeingOvered"
|
|
:is-dragover-from-above="isDragoverFromAbove(item.position)"
|
|
/>
|
|
</TransitionGroup>
|
|
<Pagination
|
|
class="mt-5"
|
|
:pageNumber="pageNumber"
|
|
:pageTotal="pageTotal"
|
|
maxItemDisplayed="11"
|
|
@pageChange="pageChange"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.list-move, /* apply transition to moving elements */
|
|
.list-enter-active,
|
|
.list-leave-active {
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.list-enter-from,
|
|
.list-leave-to {
|
|
opacity: 0;
|
|
transform: translateX(30px);
|
|
}
|
|
</style>
|