[FEAT] Add sharing function, UI enhancement

This commit is contained in:
2025-12-14 19:06:55 +01:00
parent 883c93b7ff
commit a381900356
9 changed files with 664 additions and 59 deletions

View File

@@ -0,0 +1,119 @@
<template>
<Modal :is-open="isOpen" @close="close" title="Share spritesheet" :initialWidth="500" :initialHeight="280">
<div class="h-full flex flex-col">
<div class="flex-1 overflow-auto p-4 space-y-4 dark:bg-gray-800">
<!-- Loading state -->
<div v-if="loading" class="flex flex-col items-center justify-center py-8">
<i class="fas fa-circle-notch fa-spin text-3xl text-gray-400 dark:text-gray-500 mb-3"></i>
<p class="text-sm text-gray-600 dark:text-gray-300">Uploading spritesheet...</p>
</div>
<!-- Success state -->
<div v-else-if="shareUrl" class="space-y-4">
<p class="text-sm text-gray-600 dark:text-gray-300">Your spritesheet is ready to share! Copy the link below:</p>
<div class="flex items-center gap-2">
<input type="text" :value="shareUrl" readonly class="flex-1 px-3 py-2 rounded-lg border border-gray-200 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-100 text-sm font-mono" @focus="($event.target as HTMLInputElement).select()" />
<button @click="copyToClipboard" class="px-4 py-2 rounded-lg bg-blue-500 hover:bg-blue-600 text-white text-sm font-medium transition-colors" :title="copied ? 'Copied!' : 'Copy to clipboard'">
<i :class="copied ? 'fas fa-check' : 'fas fa-copy'"></i>
</button>
</div>
<p v-if="copied" class="text-sm text-green-600 dark:text-green-400">Link copied to clipboard!</p>
</div>
<!-- Error state -->
<div v-else-if="error" class="space-y-4">
<p class="text-sm text-red-600 dark:text-red-400">{{ error }}</p>
<button @click="retry" class="px-4 py-2 rounded-lg bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-800 dark:text-gray-100 text-sm font-medium transition-colors"><i class="fas fa-redo mr-2"></i>Try again</button>
</div>
<!-- Initial state (shouldn't normally be visible) -->
<div v-else class="flex flex-col items-center justify-center py-8">
<p class="text-sm text-gray-600 dark:text-gray-300">Preparing to share...</p>
</div>
</div>
<div class="border-t border-gray-200 dark:border-gray-700 p-3 flex justify-end">
<button type="button" class="px-4 py-2 rounded-lg bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-800 dark:text-gray-100" @click="close">Close</button>
</div>
</div>
</Modal>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
import Modal from './utilities/Modal.vue';
const props = defineProps<{
isOpen: boolean;
shareFunction: () => Promise<{ id: string; url: string }>;
}>();
const emit = defineEmits<{ (e: 'close'): void }>();
const loading = ref(false);
const shareUrl = ref('');
const error = ref('');
const copied = ref(false);
const close = () => emit('close');
const performShare = async () => {
loading.value = true;
error.value = '';
shareUrl.value = '';
copied.value = false;
try {
const result = await props.shareFunction();
shareUrl.value = result.url;
} catch (e: any) {
console.error('Failed to share spritesheet:', e);
error.value = 'Failed to share spritesheet. Please try again later.';
} finally {
loading.value = false;
}
};
const retry = () => {
performShare();
};
const copyToClipboard = async () => {
try {
await navigator.clipboard.writeText(shareUrl.value);
copied.value = true;
setTimeout(() => {
copied.value = false;
}, 2000);
} catch {
// Fallback for older browsers
const input = document.createElement('input');
input.value = shareUrl.value;
document.body.appendChild(input);
input.select();
document.execCommand('copy');
document.body.removeChild(input);
copied.value = true;
setTimeout(() => {
copied.value = false;
}, 2000);
}
};
// Start sharing when modal opens
watch(
() => props.isOpen,
isOpen => {
if (isOpen) {
performShare();
} else {
// Reset state when closing
loading.value = false;
shareUrl.value = '';
error.value = '';
copied.value = false;
}
}
);
</script>

View File

@@ -1,9 +1,9 @@
<template>
<div class="spritesheet-preview w-full h-full">
<div class="flex flex-col lg:flex-row gap-4 h-full">
<div class="flex-1 min-w-0 flex flex-col">
<div class="flex flex-col lg:flex-row gap-4 h-full min-h-0">
<div class="flex-1 min-w-0 flex flex-col min-h-0">
<div
class="relative bg-gray-50 dark:bg-gray-800 border border-gray-300 dark:border-gray-700 rounded-lg overflow-auto flex-1 min-h-[300px] max-h-[calc(100vh-12rem)] shadow-sm hover:shadow-md transition-shadow duration-200"
class="relative bg-gray-50 dark:bg-gray-800 border border-gray-300 dark:border-gray-700 rounded-lg overflow-auto flex-1 min-h-[200px] sm:min-h-[300px] max-h-[50vh] lg:max-h-none shadow-sm hover:shadow-md transition-shadow duration-200"
@mousemove="drag"
@mouseup="stopDrag"
@mouseleave="stopDrag"
@@ -82,10 +82,10 @@
</div>
</div>
<div class="lg:w-80 xl:w-96 flex-shrink-0">
<div class="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 shadow-sm overflow-hidden">
<div class="lg:w-80 xl:w-96 flex-shrink-0 lg:h-full lg:min-h-0 flex flex-col">
<div class="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 shadow-sm overflow-hidden flex-1 flex flex-col lg:overflow-y-auto">
<!-- Playback Controls -->
<div class="p-4 border-b border-gray-100 dark:border-gray-700">
<div class="p-4 border-b border-gray-100 dark:border-gray-700 flex-shrink-0">
<h3 class="text-xs font-bold text-gray-400 dark:text-gray-500 uppercase tracking-wider mb-3">Playback</h3>
<div class="flex items-center gap-2">
<button @click="togglePlayback" class="flex items-center justify-center gap-2 bg-blue-600 hover:bg-blue-700 text-white px-4 py-2.5 rounded-lg transition-all cursor-pointer flex-1 shadow-sm active:scale-95">
@@ -108,7 +108,7 @@
</div>
<!-- Animation Settings -->
<div class="p-4 border-b border-gray-100 dark:border-gray-700 space-y-5">
<div class="p-4 border-b border-gray-100 dark:border-gray-700 space-y-5 flex-shrink-0">
<h3 class="text-xs font-bold text-gray-400 dark:text-gray-500 uppercase tracking-wider mb-1">Animation</h3>
<!-- Frame Navigation -->
@@ -131,7 +131,7 @@
</div>
<!-- View Options -->
<div class="p-4 space-y-5">
<div class="p-4 space-y-5 flex-1">
<h3 class="text-xs font-bold text-gray-400 dark:text-gray-500 uppercase tracking-wider mb-1">View Options</h3>
<!-- Zoom Control -->
@@ -185,7 +185,7 @@
</div>
<!-- Current frame offset display -->
<div v-if="currentFrameSprite" class="px-4 pb-4">
<div v-if="currentFrameSprite" class="px-4 pb-4 flex-shrink-0">
<div class="p-3 bg-gray-50 dark:bg-gray-700/50 rounded-lg border border-gray-100 dark:border-gray-700 flex items-center justify-between">
<span class="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide">Offset</span>
<span class="text-xs font-mono font-bold text-gray-700 dark:text-gray-200">X: {{ currentFrameSprite.x }} <span class="text-gray-300 dark:text-gray-600 mx-1">|</span> Y: {{ currentFrameSprite.y }}</span>
@@ -193,7 +193,7 @@
</div>
<!-- Frame Selection (when Compare sprites is enabled) -->
<div v-if="showAllSprites" class="border-t border-gray-100 dark:border-gray-700 p-4">
<div v-if="showAllSprites" class="border-t border-gray-100 dark:border-gray-700 p-4 flex-shrink-0">
<div class="flex items-center justify-between mb-3">
<h3 class="text-xs font-bold text-gray-400 dark:text-gray-500 uppercase tracking-wider">Visible Frames</h3>
<div class="flex gap-1">
@@ -513,4 +513,36 @@
cursor: pointer;
border: none;
}
/* Custom scrollbar for controls panel */
.custom-scrollbar::-webkit-scrollbar,
.lg\:overflow-y-auto::-webkit-scrollbar {
width: 6px;
}
.custom-scrollbar::-webkit-scrollbar-track,
.lg\:overflow-y-auto::-webkit-scrollbar-track {
background: transparent;
}
.custom-scrollbar::-webkit-scrollbar-thumb,
.lg\:overflow-y-auto::-webkit-scrollbar-thumb {
background-color: rgba(156, 163, 175, 0.5);
border-radius: 3px;
}
.custom-scrollbar::-webkit-scrollbar-thumb:hover,
.lg\:overflow-y-auto::-webkit-scrollbar-thumb:hover {
background-color: rgba(156, 163, 175, 0.8);
}
:global(.dark) .custom-scrollbar::-webkit-scrollbar-thumb,
:global(.dark) .lg\:overflow-y-auto::-webkit-scrollbar-thumb {
background-color: rgba(75, 85, 99, 0.5);
}
:global(.dark) .custom-scrollbar::-webkit-scrollbar-thumb:hover,
:global(.dark) .lg\:overflow-y-auto::-webkit-scrollbar-thumb:hover {
background-color: rgba(75, 85, 99, 0.8);
}
</style>