144 lines
5.7 KiB
Vue

<script setup>
import { ref, computed, nextTick, watch } from 'vue';
import GoogleIcon from '@Shared/GoogleIcon.vue';
import PageSizeSelector from '@Holos/PDF/PageSizeSelector.vue';
/** Propiedades */
const props = defineProps({
pages: { type: Array, default: () => [] },
currentPage: { type: Number, default: 1 }
});
/** Eventos */
const emit = defineEmits(['drop', 'add-page', 'delete-page', 'page-change', 'page-size-change', 'click']);
/** Referencias */
const pageSize = ref('A4');
const pageSizes = {
'A4': { width: 794, height: 1123, label: 'A4 (210 x 297 mm)' },
'Letter': { width: 816, height: 1056, label: 'Carta (216 x 279 mm)' },
'A3': { width: 1123, height: 1587, label: 'A3 (297 x 420 mm)' },
'Legal': { width: 816, height: 1344, label: 'Oficio (216 x 356 mm)' },
'Tabloid': { width: 1056, height: 1632, label: 'Tabloide (279 x 432 mm)' }
};
const ZOOM_LEVEL = 0.65;
/** Propiedades computadas */
const currentPageSize = computed(() => pageSizes[pageSize.value] || pageSizes['A4']);
const scaledPageWidth = computed(() => currentPageSize.value.width * ZOOM_LEVEL);
const scaledPageHeight = computed(() => currentPageSize.value.height * ZOOM_LEVEL);
const totalPages = computed(() => props.pages.length);
watch(pageSize, (newSize) => {
emit('page-size-change', { size: newSize, dimensions: pageSizes[newSize] });
});
const handleDrop = (event, pageIndex) => {
event.preventDefault();
const rect = event.currentTarget.getBoundingClientRect();
const x = (event.clientX - rect.left) / ZOOM_LEVEL;
const y = (event.clientY - rect.top) / ZOOM_LEVEL;
emit('drop', { originalEvent: event, pageIndex, x, y });
};
const handleDragOver = (event) => {
event.preventDefault();
};
const setCurrentPage = (pageNumber) => {
emit('page-change', pageNumber);
};
const addPageAndNavigate = () => {
emit('add-page');
nextTick(() => {
setCurrentPage(totalPages.value);
});
};
const handleNextPage = () => {
if (props.currentPage >= totalPages.value) {
addPageAndNavigate();
} else {
setCurrentPage(props.currentPage + 1);
}
};
const deletePage = (pageIndex) => {
if (totalPages.value > 1) {
emit('delete-page', pageIndex);
}
};
</script>
<template>
<div class="flex-1 flex flex-col bg-gray-100">
<div class="flex items-center justify-between px-4 py-3 bg-white border-b">
<div class="flex items-center gap-4">
<span class="text-sm font-medium text-gray-700">
Página {{ currentPage }} de {{ totalPages }}
</span>
<div class="flex items-center gap-1 border-l pl-4">
<button
@click="setCurrentPage(Math.max(1, currentPage - 1))"
:disabled="currentPage <= 1"
class="p-2 text-gray-500 hover:text-gray-700 disabled:opacity-50 disabled:cursor-not-allowed"
title="Página anterior"
>
<GoogleIcon name="keyboard_arrow_left" class="text-xl" />
</button>
<button
@click="handleNextPage"
class="p-2 text-gray-500 hover:text-gray-700"
:title="currentPage >= totalPages ? 'Crear nueva página' : 'Página siguiente'"
>
<GoogleIcon name="keyboard_arrow_right" class="text-xl" />
</button>
</div>
</div>
<div class="flex-shrink-0">
<PageSizeSelector v-model="pageSize" />
</div>
</div>
<div class="flex-1 overflow-auto p-8" @click="$emit('click', $event)">
<div class="flex items-start justify-center gap-8 min-h-full">
<div v-for="(page, pageIndex) in pages" :key="page.id" class="relative group flex-shrink-0">
<div class="absolute -top-6 left-1/2 transform -translate-x-1/2 text-xs text-gray-500 whitespace-nowrap">
Página {{ pageIndex + 1 }}
</div>
<button
v-if="totalPages > 1"
@click="deletePage(pageIndex)"
class="absolute -top-6 right-0 w-6 h-6 bg-white rounded-full text-red-500 opacity-0 group-hover:opacity-100 flex items-center justify-center shadow-md hover:shadow-lg transition-all z-10"
title="Eliminar página"
>
<GoogleIcon name="delete" class="text-sm" />
</button>
<div
class="pdf-page relative bg-white shadow-lg rounded-md border transition-all duration-200 overflow-hidden"
:class="{
'ring-2 ring-blue-500': currentPage === pageIndex + 1,
'hover:shadow-xl': currentPage !== pageIndex + 1
}"
:style="{ width: `${scaledPageWidth}px`, height: `${scaledPageHeight}px` }"
@drop="(e) => handleDrop(e, pageIndex)"
@dragover="handleDragOver"
@click="setCurrentPage(pageIndex + 1)"
>
<div
class="absolute inset-0"
:style="{ transform: `scale(${ZOOM_LEVEL})`, transformOrigin: 'top left' }"
>
<slot name="elements" :page="page" />
</div>
</div>
</div>
</div>
</div>
</div>
</template>