Improved UX
This commit is contained in:
84
src/App.vue
84
src/App.vue
@@ -83,14 +83,6 @@
|
||||
<!-- Left sidebar - Controls -->
|
||||
<div class="border-r border-gray-200 dark:border-gray-700 p-6 overflow-y-auto max-h-[calc(100vh-200px)] bg-gray-50/50 dark:bg-gray-900/30">
|
||||
<div class="space-y-6">
|
||||
<button
|
||||
@click="openPreviewModal"
|
||||
class="col-span-2 px-3 py-2 bg-green-500 hover:bg-green-600 dark:bg-green-600 dark:hover:bg-green-700 text-white rounded-lg transition-all text-xs font-medium flex items-center justify-center gap-1.5 cursor-pointer"
|
||||
data-rybbit-event="preview-animation"
|
||||
>
|
||||
<i class="fas fa-play"></i>
|
||||
<span>Preview animation</span>
|
||||
</button>
|
||||
<!-- Upload Section -->
|
||||
<section>
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
@@ -181,12 +173,26 @@
|
||||
<span class="text-sm font-medium text-gray-700 dark:text-gray-200">Manual size</span>
|
||||
<input type="checkbox" v-model="settingsStore.manualCellSizeEnabled" class="w-4 h-4 rounded" />
|
||||
</label>
|
||||
<div v-if="settingsStore.manualCellSizeEnabled" class="flex items-center gap-2 mt-2">
|
||||
<input type="number" v-model.number="settingsStore.manualCellWidth" min="1" max="2048" class="flex-1 px-2 py-1 border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-100 rounded text-sm focus:ring-2 focus:ring-blue-500 outline-none" placeholder="W" />
|
||||
<span class="text-gray-500 dark:text-gray-400">×</span>
|
||||
<input type="number" v-model.number="settingsStore.manualCellHeight" min="1" max="2048" class="flex-1 px-2 py-1 border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-100 rounded text-sm focus:ring-2 focus:ring-blue-500 outline-none" placeholder="H" />
|
||||
<div v-if="settingsStore.manualCellSizeEnabled" class="flex items-center gap-1.5 mt-2">
|
||||
<input
|
||||
type="number"
|
||||
v-model.number="settingsStore.manualCellWidth"
|
||||
min="1"
|
||||
max="2048"
|
||||
class="w-full min-w-0 px-2 py-1 border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-100 rounded text-sm focus:ring-2 focus:ring-blue-500 outline-none"
|
||||
placeholder="W"
|
||||
/>
|
||||
<span class="text-gray-500 dark:text-gray-400 flex-shrink-0">×</span>
|
||||
<input
|
||||
type="number"
|
||||
v-model.number="settingsStore.manualCellHeight"
|
||||
min="1"
|
||||
max="2048"
|
||||
class="w-full min-w-0 px-2 py-1 border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-100 rounded text-sm focus:ring-2 focus:ring-blue-500 outline-none"
|
||||
placeholder="H"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="text-xs text-gray-500 dark:text-gray-400 font-mono mt-1">{{ cellSize.width }} × {{ cellSize.height }}px</div>
|
||||
<div v-else class="text-xs text-gray-500 dark:text-gray-400 font-mono mt-1 break-words">{{ cellSize.width }} × {{ cellSize.height }}px</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -247,18 +253,41 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right panel - Canvas Preview -->
|
||||
<div class="p-6 overflow-y-auto overflow-x-auto max-h-[calc(100vh-200px)]">
|
||||
<sprite-canvas :layers="layers" :active-layer-id="activeLayerId" :columns="columns" @update-sprite="updateSpritePosition" @update-sprite-cell="updateSpriteCell" @remove-sprite="removeSprite" @replace-sprite="replaceSprite" @add-sprite="addSprite" />
|
||||
<!-- Right panel - Tabs -->
|
||||
<div class="flex flex-col min-w-0">
|
||||
<!-- Tab Navigation -->
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 bg-gray-50/50 dark:bg-gray-900/30">
|
||||
<div class="flex gap-1 p-2">
|
||||
<button
|
||||
@click="activeTab = 'canvas'"
|
||||
:class="['flex items-center gap-2 px-4 py-2 rounded-lg transition-all text-sm font-medium', activeTab === 'canvas' ? 'bg-white dark:bg-gray-800 text-blue-600 dark:text-blue-400 shadow-sm' : 'text-gray-600 dark:text-gray-400 hover:bg-white/50 dark:hover:bg-gray-800/50']"
|
||||
>
|
||||
<i class="fas fa-th"></i>
|
||||
<span>Canvas</span>
|
||||
</button>
|
||||
<button
|
||||
@click="activeTab = 'preview'"
|
||||
:class="['flex items-center gap-2 px-4 py-2 rounded-lg transition-all text-sm font-medium', activeTab === 'preview' ? 'bg-white dark:bg-gray-800 text-blue-600 dark:text-blue-400 shadow-sm' : 'text-gray-600 dark:text-gray-400 hover:bg-white/50 dark:hover:bg-gray-800/50']"
|
||||
data-rybbit-event="preview-animation"
|
||||
>
|
||||
<i class="fas fa-play"></i>
|
||||
<span>Preview</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tab Content -->
|
||||
<div class="p-6 overflow-y-auto overflow-x-hidden max-h-[calc(100vh-260px)] min-w-0">
|
||||
<div class="max-w-full">
|
||||
<sprite-canvas v-show="activeTab === 'canvas'" :layers="layers" :active-layer-id="activeLayerId" :columns="columns" @update-sprite="updateSpritePosition" @update-sprite-cell="updateSpriteCell" @remove-sprite="removeSprite" @replace-sprite="replaceSprite" @add-sprite="addSprite" />
|
||||
<sprite-preview v-show="activeTab === 'preview'" :layers="layers" :active-layer-id="activeLayerId" :columns="columns" @update-sprite="updateSpritePosition" @update-sprite-in-layer="updateSpriteInLayer" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<Modal :is-open="isPreviewModalOpen" @close="closePreviewModal" title="Animation Preview">
|
||||
<sprite-preview :layers="layers" :active-layer-id="activeLayerId" :columns="columns" @update-sprite="updateSpritePosition" @update-sprite-in-layer="updateSpriteInLayer" />
|
||||
</Modal>
|
||||
|
||||
<HelpModal :is-open="isHelpModalOpen" @close="closeHelpModal" />
|
||||
<FeedbackModal :is-open="isFeedbackModalOpen" @close="closeFeedbackModal" />
|
||||
<SpritesheetSplitter :is-open="isSpritesheetSplitterOpen" :image-url="spritesheetImageUrl" :image-file="spritesheetImageFile" @close="closeSpritesheetSplitter" @split="handleSplitSpritesheet" />
|
||||
@@ -285,7 +314,6 @@
|
||||
import { ref, onMounted, toRef, computed } from 'vue';
|
||||
import FileUploader from './components/FileUploader.vue';
|
||||
import SpriteCanvas from './components/SpriteCanvas.vue';
|
||||
import Modal from './components/utilities/Modal.vue';
|
||||
import SpritePreview from './components/SpritePreview.vue';
|
||||
import HelpModal from './components/HelpModal.vue';
|
||||
import FeedbackModal from './components/FeedbackModal.vue';
|
||||
@@ -327,7 +355,7 @@
|
||||
};
|
||||
|
||||
const cellSize = computed(getCellSize);
|
||||
const isPreviewModalOpen = ref(false);
|
||||
const activeTab = ref<'canvas' | 'preview'>('canvas');
|
||||
const isHelpModalOpen = ref(false);
|
||||
const isFeedbackModalOpen = ref(false);
|
||||
const isSpritesheetSplitterOpen = ref(false);
|
||||
@@ -380,18 +408,6 @@
|
||||
}
|
||||
};
|
||||
|
||||
const openPreviewModal = () => {
|
||||
if (!visibleLayers.value.some(l => l.sprites.length > 0)) {
|
||||
alert('Please upload or import sprites to preview an animation.');
|
||||
return;
|
||||
}
|
||||
isPreviewModalOpen.value = true;
|
||||
};
|
||||
|
||||
const closePreviewModal = () => {
|
||||
isPreviewModalOpen.value = false;
|
||||
};
|
||||
|
||||
const openHelpModal = () => {
|
||||
isHelpModalOpen.value = true;
|
||||
};
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<template>
|
||||
<div class="space-y-6 relative">
|
||||
<div class="space-y-6 w-full max-w-full overflow-hidden">
|
||||
<div class="bg-cyan-500 dark:bg-cyan-600 rounded-xl p-4 shadow-lg border border-cyan-400/50 dark:border-cyan-500/50">
|
||||
<div class="flex items-start gap-3">
|
||||
<i class="fas fa-lightbulb text-yellow-300 text-xl mt-0.5"></i>
|
||||
<div>
|
||||
<i class="fas fa-lightbulb text-yellow-300 text-xl mt-0.5 flex-shrink-0"></i>
|
||||
<div class="min-w-0">
|
||||
<h4 class="font-semibold text-white mb-1">Tip from developer</h4>
|
||||
<p class="text-cyan-50 text-sm">Right-click any sprite to open the context menu for quick actions: add, replace, or remove sprites.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section>
|
||||
<section class="w-full">
|
||||
<h3 class="text-lg font-bold text-gray-800 dark:text-gray-100 mb-4 flex items-center gap-2">
|
||||
<i class="fas fa-cog text-blue-600 dark:text-blue-400"></i>
|
||||
Canvas options
|
||||
@@ -62,7 +62,7 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="relative bg-white dark:bg-gray-800 border-2 border-gray-200 dark:border-gray-700 rounded-2xl shadow-lg overflow-auto max-h-[calc(100vh-400px)] min-h-[500px]">
|
||||
<div class="relative bg-white dark:bg-gray-800 border-2 border-gray-200 dark:border-gray-700 rounded-2xl shadow-lg overflow-auto max-h-[calc(100vh-400px)] min-h-[500px] w-full">
|
||||
<div class="canvas-container touch-manipulation relative inline-block min-w-full">
|
||||
<div :style="{ transform: `scale(${zoom})`, transformOrigin: 'top left' }" class="inline-block">
|
||||
<canvas
|
||||
|
||||
Reference in New Issue
Block a user