201 lines
6.9 KiB
Vue
201 lines
6.9 KiB
Vue
<script setup>
|
||
import { ref, computed, watch } from 'vue';
|
||
import GoogleIcon from '@Shared/GoogleIcon.vue';
|
||
|
||
const props = defineProps({
|
||
element: {
|
||
type: Object,
|
||
required: true
|
||
},
|
||
isEditing: {
|
||
type: Boolean,
|
||
default: false
|
||
}
|
||
});
|
||
|
||
const emit = defineEmits(['update', 'finish-editing']);
|
||
|
||
// Estructura simple: empezar con una celda
|
||
const tableData = ref([]);
|
||
|
||
// Watcher para sincronizar datos
|
||
watch(() => props.element.content, (newContent) => {
|
||
if (newContent && newContent.data && Array.isArray(newContent.data)) {
|
||
tableData.value = JSON.parse(JSON.stringify(newContent.data));
|
||
} else {
|
||
// Inicializar con una sola celda
|
||
tableData.value = [['']];
|
||
}
|
||
}, { immediate: true });
|
||
|
||
const rows = computed(() => tableData.value.length);
|
||
const cols = computed(() => tableData.value[0]?.length || 1);
|
||
|
||
// Agregar fila
|
||
const addRow = () => {
|
||
const newRow = new Array(cols.value).fill('');
|
||
tableData.value.push(newRow);
|
||
updateTable();
|
||
};
|
||
|
||
// Agregar columna
|
||
const addColumn = () => {
|
||
tableData.value.forEach(row => {
|
||
row.push('');
|
||
});
|
||
updateTable();
|
||
};
|
||
|
||
// Eliminar fila (no permitir si solo hay una)
|
||
const removeRow = (rowIndex) => {
|
||
if (tableData.value.length > 1) {
|
||
tableData.value.splice(rowIndex, 1);
|
||
updateTable();
|
||
}
|
||
};
|
||
|
||
// Eliminar columna (no permitir si solo hay una)
|
||
const removeColumn = (colIndex) => {
|
||
if (tableData.value[0]?.length > 1) {
|
||
tableData.value.forEach(row => {
|
||
row.splice(colIndex, 1);
|
||
});
|
||
updateTable();
|
||
}
|
||
};
|
||
|
||
// Actualizar celda
|
||
const updateCell = (rowIndex, colIndex, value) => {
|
||
if (tableData.value[rowIndex]) {
|
||
tableData.value[rowIndex][colIndex] = value;
|
||
updateTable();
|
||
}
|
||
};
|
||
|
||
// Actualizar tabla
|
||
const updateTable = () => {
|
||
const updatedContent = {
|
||
data: JSON.parse(JSON.stringify(tableData.value)),
|
||
rows: tableData.value.length,
|
||
cols: tableData.value[0]?.length || 1
|
||
};
|
||
|
||
// Calcular dimensiones dinámicas basadas en contenido
|
||
const cellWidth = 120;
|
||
const cellHeight = 35;
|
||
const newWidth = Math.max(120, updatedContent.cols * cellWidth);
|
||
const newHeight = Math.max(40, updatedContent.rows * cellHeight);
|
||
|
||
emit('update', {
|
||
id: props.element.id,
|
||
content: updatedContent,
|
||
width: newWidth,
|
||
height: newHeight
|
||
});
|
||
};
|
||
|
||
const finishEditing = () => {
|
||
emit('finish-editing');
|
||
};
|
||
</script>
|
||
|
||
<template>
|
||
<div class="w-full h-full bg-white rounded border border-gray-300 overflow-hidden">
|
||
<!-- Controles de tabla (solo cuando está editando) -->
|
||
<div
|
||
v-if="isEditing"
|
||
class="flex items-center gap-2 p-2 bg-gray-50 border-b"
|
||
>
|
||
<button
|
||
@click="addRow"
|
||
class="px-2 py-1 text-xs bg-green-500 hover:bg-green-600 text-white rounded flex items-center gap-1"
|
||
title="Agregar fila"
|
||
>
|
||
<GoogleIcon name="add" class="text-xs" />
|
||
+
|
||
</button>
|
||
|
||
<button
|
||
@click="addColumn"
|
||
class="px-2 py-1 text-xs bg-blue-500 hover:bg-blue-600 text-white rounded flex items-center gap-1"
|
||
title="Agregar columna"
|
||
>
|
||
<GoogleIcon name="add" class="text-xs" />
|
||
+
|
||
</button>
|
||
|
||
<div class="flex-1"></div>
|
||
|
||
<span class="text-xs text-gray-500">
|
||
{{ rows }}x{{ cols }}
|
||
</span>
|
||
|
||
<button
|
||
@click="finishEditing"
|
||
class="px-2 py-1 text-xs bg-gray-600 hover:bg-gray-700 text-white rounded"
|
||
>
|
||
Cerrar
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Tabla -->
|
||
<div class="w-full h-full">
|
||
<table class="w-full h-full border-collapse text-sm">
|
||
<tbody>
|
||
<tr
|
||
v-for="(row, rowIndex) in tableData"
|
||
:key="`row-${rowIndex}`"
|
||
class="group"
|
||
:style="{ height: `${100/rows}%` }"
|
||
>
|
||
<td
|
||
v-for="(cell, colIndex) in row"
|
||
:key="`cell-${rowIndex}-${colIndex}`"
|
||
class="border border-gray-300 p-1 relative group/cell"
|
||
:style="{ width: `${100/cols}%` }"
|
||
>
|
||
<!-- Controles de celda (solo cuando está editando) -->
|
||
<div
|
||
v-if="isEditing && (rows > 1 || cols > 1)"
|
||
class="absolute -top-2 -right-2 opacity-0 group-hover/cell:opacity-100 transition-opacity z-10"
|
||
>
|
||
<!-- Eliminar fila -->
|
||
<button
|
||
v-if="rows > 1 && colIndex === 0"
|
||
@click="removeRow(rowIndex)"
|
||
class="w-4 h-4 bg-red-500 text-white rounded-full text-xs flex items-center justify-center mr-1"
|
||
title="Eliminar fila"
|
||
>
|
||
×
|
||
</button>
|
||
<!-- Eliminar columna -->
|
||
<button
|
||
v-if="cols > 1 && rowIndex === 0"
|
||
@click="removeColumn(colIndex)"
|
||
class="w-4 h-4 bg-red-500 text-white rounded-full text-xs flex items-center justify-center"
|
||
title="Eliminar columna"
|
||
>
|
||
×
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Input de celda -->
|
||
<input
|
||
v-if="isEditing"
|
||
:value="cell"
|
||
@input="updateCell(rowIndex, colIndex, $event.target.value)"
|
||
class="w-full h-full bg-transparent outline-none text-center text-sm resize-none"
|
||
@click.stop
|
||
:placeholder="rowIndex === 0 && colIndex === 0 && !cell ? 'Escribe aquí...' : ''"
|
||
/>
|
||
<!-- Vista de solo lectura -->
|
||
<div v-else class="w-full h-full flex items-center justify-center text-center text-sm p-1">
|
||
{{ cell || (rowIndex === 0 && colIndex === 0 ? 'Tabla' : '') }}
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</template> |