[FEAT] More UI enhancements
This commit is contained in:
@@ -83,121 +83,127 @@
|
||||
</div>
|
||||
|
||||
<div class="lg:w-80 xl:w-96 flex-shrink-0">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 p-4 space-y-4">
|
||||
<div class="space-y-3">
|
||||
<h3 class="text-sm font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wide">Playback</h3>
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 shadow-sm overflow-hidden">
|
||||
<!-- Playback Controls -->
|
||||
<div class="p-4 border-b border-gray-100 dark:border-gray-700">
|
||||
<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-1.5 bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-md transition-colors cursor-pointer flex-1">
|
||||
<span v-if="isPlaying" class="flex items-center gap-1.5">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-5 h-5">
|
||||
<path fill-rule="evenodd" d="M6.75 5.25a.75.75 0 01.75-.75H9a.75.75 0 01.75.75v13.5a.75.75 0 01-.75.75H7.5a.75.75 0 01-.75-.75V5.25zm7.5 0A.75.75 0 0115 4.5h1.5a.75.75 0 01.75.75v13.5a.75.75 0 01-.75.75H15a.75.75 0 01-.75-.75V5.25z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
<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">
|
||||
<span v-if="isPlaying" class="flex items-center gap-2 font-medium">
|
||||
<i class="fas fa-pause"></i>
|
||||
Pause
|
||||
</span>
|
||||
<span v-else class="flex items-center gap-1.5">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-5 h-5">
|
||||
<path fill-rule="evenodd" d="M4.5 5.653c0-1.426 1.529-2.33 2.779-1.643l11.54 6.348c1.295.712 1.295 2.573 0 3.285L7.28 19.991c-1.25.687-2.779-.217-2.779-1.643V5.653z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
<span v-else class="flex items-center gap-2 font-medium">
|
||||
<i class="fas fa-play"></i>
|
||||
Play
|
||||
</span>
|
||||
</button>
|
||||
<button @click="previousFrame" class="bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600 p-2 rounded-md transition-colors duration-200 cursor-pointer" :disabled="isPlaying" :class="{ 'opacity-50 cursor-not-allowed': isPlaying }">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-5 h-5 dark:text-gray-200">
|
||||
<path fill-rule="evenodd" d="M11.78 5.22a.75.75 0 01.75.75v12.06l4.72-4.72a.75.75 0 111.06 1.06l-6 6a.75.75 0 01-1.06 0l-6-6a.75.75 0 011.06-1.06l4.72 4.72V5.97a.75.75 0 01.75-.75z" clip-rule="evenodd" transform="rotate(90 12 12)" />
|
||||
</svg>
|
||||
<button @click="previousFrame" class="btn btn-secondary btn-icon rounded-lg" :disabled="isPlaying" :class="{ 'opacity-50 cursor-not-allowed': isPlaying }">
|
||||
<i class="fas fa-step-backward"></i>
|
||||
</button>
|
||||
<button @click="nextFrame" class="bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600 p-2 rounded-md transition-colors duration-200 cursor-pointer" :disabled="isPlaying" :class="{ 'opacity-50 cursor-not-allowed': isPlaying }">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-5 h-5 dark:text-gray-200">
|
||||
<path fill-rule="evenodd" d="M11.78 5.22a.75.75 0 01.75.75v12.06l4.72-4.72a.75.75 0 111.06 1.06l-6 6a.75.75 0 01-1.06 0l-6-6a.75.75 0 011.06-1.06l4.72 4.72V5.97a.75.75 0 01.75-.75z" clip-rule="evenodd" transform="rotate(-90 12 12)" />
|
||||
</svg>
|
||||
<button @click="nextFrame" class="btn btn-secondary btn-icon rounded-lg" :disabled="isPlaying" :class="{ 'opacity-50 cursor-not-allowed': isPlaying }">
|
||||
<i class="fas fa-step-forward"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sliders -->
|
||||
<div class="space-y-3">
|
||||
<h3 class="text-sm font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wide">Controls</h3>
|
||||
|
||||
<!-- Animation Settings -->
|
||||
<div class="p-4 border-b border-gray-100 dark:border-gray-700 space-y-5">
|
||||
<h3 class="text-xs font-bold text-gray-400 dark:text-gray-500 uppercase tracking-wider mb-1">Animation</h3>
|
||||
|
||||
<!-- Frame Navigation -->
|
||||
<div class="space-y-1">
|
||||
<div class="space-y-2">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-xs font-medium text-gray-600 dark:text-gray-400">Frame</span>
|
||||
<span class="text-xs font-mono text-gray-700 dark:text-gray-300">{{ visibleFrameNumber }}/{{ visibleFramesCount }}</span>
|
||||
<span class="text-sm font-medium text-gray-700 dark:text-gray-300">Frame</span>
|
||||
<span class="text-xs font-mono text-gray-500 dark:text-gray-400 bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">{{ visibleFrameNumber }} / {{ visibleFramesCount }}</span>
|
||||
</div>
|
||||
<input type="range" min="0" :max="visibleFrames.length - 1" :value="visibleFrameIndex" @input="handleSliderInput" class="w-full h-2 bg-gray-200 dark:bg-gray-700 rounded-lg appearance-none cursor-pointer accent-blue-500" :disabled="isPlaying" :class="{ 'opacity-50': isPlaying }" />
|
||||
<input type="range" min="0" :max="visibleFrames.length - 1" :value="visibleFrameIndex" @input="handleSliderInput" class="w-full h-1.5 bg-gray-200 dark:bg-gray-700 rounded-lg appearance-none cursor-pointer accent-blue-600" :disabled="isPlaying" :class="{ 'opacity-50': isPlaying }" />
|
||||
</div>
|
||||
|
||||
<!-- FPS Control -->
|
||||
<div class="space-y-1">
|
||||
<div class="space-y-2">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-xs font-medium text-gray-600 dark:text-gray-400">FPS</span>
|
||||
<span class="text-xs font-mono text-gray-700 dark:text-gray-300">{{ fps }}</span>
|
||||
<span class="text-sm font-medium text-gray-700 dark:text-gray-300">Speed (FPS)</span>
|
||||
<span class="text-xs font-mono text-gray-500 dark:text-gray-400 bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">{{ fps }}</span>
|
||||
</div>
|
||||
<input type="range" min="1" max="60" v-model.number="fps" class="w-full h-2 bg-gray-200 dark:bg-gray-700 rounded-lg appearance-none cursor-pointer accent-blue-500" />
|
||||
</div>
|
||||
|
||||
<!-- Zoom Control -->
|
||||
<div class="space-y-1">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-xs font-medium text-gray-600 dark:text-gray-400">Zoom</span>
|
||||
<span class="text-xs font-mono text-gray-700 dark:text-gray-300">{{ Math.round(zoom * 100) }}%</span>
|
||||
</div>
|
||||
<input type="range" min="0.5" max="5" step="0.1" v-model.number="zoom" class="w-full h-2 bg-gray-200 dark:bg-gray-700 rounded-lg appearance-none cursor-pointer accent-blue-500" />
|
||||
<input type="range" min="1" max="60" v-model.number="fps" class="w-full h-1.5 bg-gray-200 dark:bg-gray-700 rounded-lg appearance-none cursor-pointer accent-blue-600" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Options -->
|
||||
<div class="space-y-3">
|
||||
<h3 class="text-sm font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wide">Options</h3>
|
||||
<!-- View Options -->
|
||||
<div class="p-4 space-y-5">
|
||||
<h3 class="text-xs font-bold text-gray-400 dark:text-gray-500 uppercase tracking-wider mb-1">View Options</h3>
|
||||
|
||||
<!-- Zoom Control -->
|
||||
<div class="space-y-2">
|
||||
<label class="flex items-center gap-2 cursor-pointer">
|
||||
<input type="checkbox" v-model="isDraggable" class="w-4 h-4 text-blue-500 focus:ring-blue-400 rounded border-gray-300 dark:border-gray-600 dark:bg-gray-700" />
|
||||
<span class="text-sm dark:text-gray-200">Reposition</span>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm font-medium text-gray-700 dark:text-gray-300">Zoom</span>
|
||||
<span class="text-xs font-mono text-gray-500 dark:text-gray-400 bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">{{ Math.round(zoom * 100) }}%</span>
|
||||
</div>
|
||||
<input type="range" min="0.5" max="5" step="0.1" v-model.number="zoom" class="w-full h-1.5 bg-gray-200 dark:bg-gray-700 rounded-lg appearance-none cursor-pointer accent-blue-600" />
|
||||
</div>
|
||||
|
||||
<!-- Toggles -->
|
||||
<div class="space-y-3 pt-2">
|
||||
<label class="flex items-center justify-between cursor-pointer group">
|
||||
<span class="text-sm text-gray-700 dark:text-gray-300 group-hover:text-gray-900 dark:group-hover:text-gray-100 transition-colors">Pixel perfect</span>
|
||||
<div class="relative inline-flex items-center cursor-pointer">
|
||||
<input type="checkbox" v-model="settingsStore.pixelPerfect" class="sr-only peer">
|
||||
<div class="w-9 h-5 bg-gray-200 peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="flex items-center gap-2 cursor-pointer ml-4" :class="{ 'opacity-50 cursor-not-allowed': !isDraggable }">
|
||||
<input type="checkbox" v-model="repositionAllLayers" class="w-4 h-4 text-blue-500 focus:ring-blue-400 rounded border-gray-300 dark:border-gray-600 dark:bg-gray-700" :disabled="!isDraggable" />
|
||||
<span class="text-sm dark:text-gray-200">All layers</span>
|
||||
<label class="flex items-center justify-between cursor-pointer group">
|
||||
<span class="text-sm text-gray-700 dark:text-gray-300 group-hover:text-gray-900 dark:group-hover:text-gray-100 transition-colors">Reposition mode</span>
|
||||
<div class="relative inline-flex items-center cursor-pointer">
|
||||
<input type="checkbox" v-model="isDraggable" class="sr-only peer">
|
||||
<div class="w-9 h-5 bg-gray-200 peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="flex items-center gap-2 cursor-pointer">
|
||||
<input type="checkbox" v-model="showAllSprites" class="w-4 h-4 text-blue-500 focus:ring-blue-400 rounded border-gray-300 dark:border-gray-600 dark:bg-gray-700" />
|
||||
<span class="text-sm dark:text-gray-200">Compare sprites</span>
|
||||
</label>
|
||||
<div class="pl-4 border-l-2 border-gray-100 dark:border-gray-700 transition-all" :class="{ 'opacity-50 pointer-events-none': !isDraggable }">
|
||||
<label class="flex items-center justify-between cursor-pointer group">
|
||||
<span class="text-sm text-gray-600 dark:text-gray-400 group-hover:text-gray-800 dark:group-hover:text-gray-200 transition-colors">Apply to all layers</span>
|
||||
<input type="checkbox" v-model="repositionAllLayers" class="w-4 h-4 text-blue-600 rounded border-gray-300 focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600" :disabled="!isDraggable" />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<label class="flex items-center gap-2 cursor-pointer">
|
||||
<input type="checkbox" v-model="settingsStore.pixelPerfect" class="w-4 h-4 text-blue-500 focus:ring-blue-400 rounded border-gray-300 dark:border-gray-600 dark:bg-gray-700" />
|
||||
<span class="text-sm dark:text-gray-200">Pixel perfect</span>
|
||||
<label class="flex items-center justify-between cursor-pointer group">
|
||||
<span class="text-sm text-gray-700 dark:text-gray-300 group-hover:text-gray-900 dark:group-hover:text-gray-100 transition-colors">Compare sprites</span>
|
||||
<div class="relative inline-flex items-center cursor-pointer">
|
||||
<input type="checkbox" v-model="showAllSprites" class="sr-only peer">
|
||||
<div class="w-9 h-5 bg-gray-200 peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Current frame offset display -->
|
||||
<div v-if="currentFrameSprite" class="p-2 bg-gray-100 dark:bg-gray-700 rounded-md border border-gray-200 dark:border-gray-600">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-xs font-medium text-gray-600 dark:text-gray-400">Offset</span>
|
||||
<span class="text-xs font-mono font-semibold text-cyan-600 dark:text-cyan-400">x: {{ currentFrameSprite.x }}, y: {{ currentFrameSprite.y }}</span>
|
||||
<div v-if="currentFrameSprite" class="px-4 pb-4">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Frame Selection (when Compare sprites is enabled) -->
|
||||
<div v-if="showAllSprites" class="space-y-2">
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-sm font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wide">Frames</h3>
|
||||
<div v-if="showAllSprites" class="border-t border-gray-100 dark:border-gray-700 p-4">
|
||||
<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">
|
||||
<button @click="showAllFrames" class="px-2 py-1 text-xs bg-blue-500 hover:bg-blue-600 text-white rounded transition-colors">All</button>
|
||||
<button @click="hideAllFrames" class="px-2 py-1 text-xs bg-gray-500 hover:bg-gray-600 text-white rounded transition-colors">None</button>
|
||||
<button @click="showAllFrames" class="px-2 py-1 text-[10px] font-medium bg-blue-50 text-blue-600 hover:bg-blue-100 dark:bg-blue-900/30 dark:text-blue-300 dark:hover:bg-blue-900/50 rounded transition-colors">All</button>
|
||||
<button @click="hideAllFrames" class="px-2 py-1 text-[10px] font-medium bg-gray-100 text-gray-600 hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-400 dark:hover:bg-gray-600 rounded transition-colors">None</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rounded-md border border-gray-300 dark:border-gray-600 dark:bg-gray-800">
|
||||
<div class="max-h-[180px] overflow-y-auto">
|
||||
<div class="space-y-0.5 p-1">
|
||||
<div v-for="(sprite, index) in compositeFrames" :key="sprite.id" class="flex items-center gap-2 px-2 py-1.5 hover:bg-gray-50 dark:hover:bg-gray-700 cursor-pointer rounded" @click="toggleHiddenFrame(index)">
|
||||
<input type="checkbox" :checked="!hiddenFrames.includes(index)" class="w-3.5 h-3.5 text-blue-500 focus:ring-blue-400 rounded border-gray-300 dark:border-gray-600 dark:bg-gray-700" @click.stop @change="toggleHiddenFrame(index)" />
|
||||
<div class="w-8 h-8 bg-gray-100 dark:bg-gray-700 rounded flex items-center justify-center overflow-hidden flex-shrink-0">
|
||||
<div class="bg-gray-50 dark:bg-gray-900/50 rounded-lg border border-gray-200 dark:border-gray-700 p-2">
|
||||
<div class="max-h-[150px] overflow-y-auto pr-1 custom-scrollbar">
|
||||
<div class="space-y-1">
|
||||
<div v-for="(sprite, index) in compositeFrames" :key="sprite.id" class="flex items-center gap-3 px-2 py-1.5 hover:bg-white dark:hover:bg-gray-700 cursor-pointer rounded-md transition-colors group" @click="toggleHiddenFrame(index)">
|
||||
<input type="checkbox" :checked="!hiddenFrames.includes(index)" class="w-4 h-4 text-blue-600 rounded border-gray-300 focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600" @click.stop @change="toggleHiddenFrame(index)" />
|
||||
<div class="w-8 h-8 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded flex items-center justify-center overflow-hidden flex-shrink-0 shadow-sm">
|
||||
<img :src="sprite.url" class="max-w-full max-h-full object-contain" :style="settingsStore.pixelPerfect ? { 'image-rendering': 'pixelated' } : {}" />
|
||||
</div>
|
||||
<span class="text-xs dark:text-gray-200">{{ index + 1 }}</span>
|
||||
<span class="text-xs font-mono text-gray-600 dark:text-gray-300 group-hover:text-gray-900 dark:group-hover:text-gray-100">Frame {{ index + 1 }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user