[FEAT] Enhanced UI
This commit is contained in:
@@ -45,62 +45,64 @@
|
||||
</div>
|
||||
|
||||
<!-- Two-column layout: Left controls, Right preview -->
|
||||
<div v-if="layers.some(l => l.sprites.length)" class="flex flex-col flex-1 lg:grid lg:grid-cols-[380px_1fr] xl:grid-cols-[420px_1fr] lg:overflow-hidden min-h-0">
|
||||
<!-- Left sidebar - Controls -->
|
||||
<div class="p-6 bg-gray-50/50 dark:bg-gray-900/30 border-b lg:border-b-0 lg:border-r border-gray-200 dark:border-gray-700 lg:overflow-y-auto lg:overflow-x-auto lg:min-h-0">
|
||||
<div class="space-y-8">
|
||||
<!-- Upload Section -->
|
||||
<div v-if="layers.some(l => l.sprites.length)" class="flex flex-col flex-1 lg:grid lg:grid-cols-[320px_1fr] xl:grid-cols-[360px_1fr] lg:overflow-hidden min-h-0">
|
||||
<!-- Left sidebar - Controls (TIGHT!) -->
|
||||
<div class="p-4 bg-gray-50/50 dark:bg-gray-900/30 border-b lg:border-b-0 lg:border-r border-gray-200 dark:border-gray-700 lg:overflow-y-auto lg:overflow-x-hidden lg:min-h-0">
|
||||
<div class="space-y-4">
|
||||
<!-- Add Sprites Section -->
|
||||
<section>
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<h3 class="flex items-center gap-2 text-base font-bold text-gray-800 dark:text-gray-100">
|
||||
<i class="text-sm text-gray-700 dark:text-gray-300 fas fa-upload"></i>
|
||||
Upload
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<h3 class="flex items-center gap-1.5 text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wide">
|
||||
<i class="fas fa-plus-circle text-[10px]"></i>
|
||||
Add Sprites
|
||||
</h3>
|
||||
<button @click="openJSONImportDialog" class="btn btn-dark btn-sm" data-rybbit-event="import-json">
|
||||
<i class="text-xs fas fa-file-import"></i>
|
||||
<span>JSON</span>
|
||||
<div class="flex-1 h-px bg-gray-200 dark:bg-gray-700"></div>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
class="flex-1 p-3 text-center border border-dashed rounded-lg transition-all cursor-pointer focus:outline-none group"
|
||||
:class="[isDragging ? 'border-blue-500 bg-blue-50 dark:bg-blue-900/20' : 'border-gray-300 dark:border-gray-600 hover:border-gray-500 hover:bg-gray-50 dark:hover:bg-gray-800/50']"
|
||||
@click="openFileDialog"
|
||||
@dragenter.prevent="isDragging = true"
|
||||
@dragleave.prevent="isDragging = false"
|
||||
@dragover.prevent
|
||||
@drop.prevent="handleDrop"
|
||||
>
|
||||
<i class="fas fa-image text-lg mb-1 transition-colors" :class="[isDragging ? 'text-blue-500' : 'text-gray-400 group-hover:text-gray-600']"></i>
|
||||
<p class="text-xs font-medium" :class="[isDragging ? 'text-blue-600' : 'text-gray-500 group-hover:text-gray-700']">
|
||||
{{ isDragging ? 'Drop here' : 'Images' }}
|
||||
</p>
|
||||
</button>
|
||||
<button @click="openJSONImportDialog" class="flex-1 p-3 text-center border border-gray-300 dark:border-gray-600 rounded-lg hover:border-gray-500 hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-all cursor-pointer group" data-rybbit-event="import-json">
|
||||
<i class="fas fa-file-code text-lg mb-1 text-gray-400 group-hover:text-gray-600 transition-colors"></i>
|
||||
<p class="text-xs font-medium text-gray-500 group-hover:text-gray-700">Import JSON</p>
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
class="w-full p-6 text-center border-2 border-dashed rounded-xl transition-all cursor-pointer focus:outline-none focus:ring-2 focus:ring-gray-500 group"
|
||||
:class="[isDragging ? 'border-blue-500 dark:border-blue-400 bg-blue-50 dark:bg-blue-900/20' : 'border-gray-300 dark:border-gray-600 hover:border-gray-500 dark:hover:border-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800/50']"
|
||||
@click="openFileDialog"
|
||||
@dragenter.prevent="isDragging = true"
|
||||
@dragleave.prevent="isDragging = false"
|
||||
@dragover.prevent
|
||||
@drop.prevent="handleDrop"
|
||||
>
|
||||
<i class="fas fa-plus-circle text-3xl mb-3 transition-colors" :class="[isDragging ? 'text-blue-500 dark:text-blue-400' : 'text-gray-400 dark:text-gray-500 group-hover:text-gray-600 dark:group-hover:text-gray-300']"></i>
|
||||
<p class="text-sm font-medium transition-colors" :class="[isDragging ? 'text-blue-600 dark:text-blue-300' : 'text-gray-600 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-gray-200']">
|
||||
<span v-if="isDragging">Drop files here</span>
|
||||
<span v-else>Add sprites</span>
|
||||
</p>
|
||||
</button>
|
||||
<input ref="uploadInput" type="file" multiple accept="image/*" class="hidden" @change="handleUploadChange" />
|
||||
<input ref="jsonFileInput" type="file" accept=".json,application/json" class="hidden" @change="handleJSONFileChange" />
|
||||
</section>
|
||||
|
||||
<!-- Layers -->
|
||||
<!-- Layers Section -->
|
||||
<section>
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<h3 class="flex items-center gap-2 text-base font-bold text-gray-800 dark:text-gray-100">
|
||||
<i class="text-sm text-gray-700 dark:text-gray-300 fas fa-layer-group"></i>
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<h3 class="flex items-center gap-1.5 text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wide">
|
||||
<i class="fas fa-layer-group text-[10px]"></i>
|
||||
Layers
|
||||
</h3>
|
||||
<button @click="addLayer()" class="btn btn-dark btn-sm">
|
||||
<i class="text-xs fas fa-plus"></i>
|
||||
<span>Add</span>
|
||||
<div class="flex-1 h-px bg-gray-200 dark:bg-gray-700"></div>
|
||||
<button @click="addLayer()" class="text-xs px-2 py-0.5 bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 rounded text-gray-600 dark:text-gray-300 transition-colors" title="Add new layer">
|
||||
<i class="fas fa-plus text-[9px]"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<div class="space-y-1">
|
||||
<div
|
||||
v-for="layer in layers"
|
||||
:key="layer.id"
|
||||
class="flex items-center gap-2 px-3 py-2 bg-white dark:bg-gray-800 border rounded-lg transition-all"
|
||||
:class="[layer.id === activeLayerId ? 'border-gray-800 ring-1 ring-gray-800 dark:border-gray-400 dark:ring-gray-400' : 'border-gray-200 dark:border-gray-700', !layer.visible ? 'opacity-50' : '']"
|
||||
class="flex items-center gap-1.5 px-2 py-1.5 bg-white dark:bg-gray-800 border rounded-md transition-all text-sm"
|
||||
:class="[layer.id === activeLayerId ? 'border-gray-700 ring-1 ring-gray-700 dark:border-gray-400 dark:ring-gray-400' : 'border-gray-200 dark:border-gray-700', !layer.visible ? 'opacity-40' : '']"
|
||||
>
|
||||
<button @click.stop="layer.visible = !layer.visible" class="btn btn-ghost btn-icon-sm rounded" :title="layer.visible ? 'Hide layer' : 'Show layer'">
|
||||
<i :class="layer.visible ? 'text-sm text-gray-800 dark:text-gray-200 fas fa-eye' : 'text-sm text-gray-400 dark:text-gray-500 fas fa-eye-slash'"></i>
|
||||
<button @click.stop="layer.visible = !layer.visible" class="p-1 rounded hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors" :title="layer.visible ? 'Hide' : 'Show'">
|
||||
<i :class="[layer.visible ? 'fas fa-eye text-gray-600 dark:text-gray-300' : 'fas fa-eye-slash text-gray-400', 'text-xs']"></i>
|
||||
</button>
|
||||
<input
|
||||
v-if="editingLayerId === layer.id"
|
||||
@@ -109,113 +111,129 @@
|
||||
@blur="finishEditingLayer"
|
||||
@keyup.enter="finishEditingLayer"
|
||||
@keyup.esc="cancelEditingLayer"
|
||||
class="flex-1 px-2 py-1 text-sm border border-gray-800 dark:border-gray-400 dark:bg-gray-700 dark:text-gray-100 rounded outline-none focus:ring-2 focus:ring-gray-800 dark:focus:ring-gray-400"
|
||||
class="flex-1 px-1.5 py-0.5 text-xs border border-gray-700 dark:border-gray-400 dark:bg-gray-700 dark:text-gray-100 rounded outline-none min-w-0"
|
||||
ref="layerNameInput"
|
||||
@click.stop
|
||||
/>
|
||||
<button v-else @click="activeLayerId = layer.id" class="flex-1 px-2 py-1 text-sm font-medium text-left rounded transition-all cursor-pointer" :class="layer.id === activeLayerId ? 'text-gray-900 dark:text-gray-100' : 'text-gray-700 dark:text-gray-300'">
|
||||
{{ layer.name }}
|
||||
<span v-if="layer.sprites.length" class="ml-1 text-xs opacity-60">({{ layer.sprites.length }})</span>
|
||||
<button v-else @click="activeLayerId = layer.id" class="flex-1 text-xs font-medium text-left truncate cursor-pointer min-w-0" :class="layer.id === activeLayerId ? 'text-gray-900 dark:text-gray-100' : 'text-gray-600 dark:text-gray-400'">
|
||||
{{ layer.name }}<span v-if="layer.sprites.length" class="ml-1 opacity-50">({{ layer.sprites.length }})</span>
|
||||
</button>
|
||||
<button v-if="editingLayerId !== layer.id" @click="startEditingLayer(layer.id, layer.name)" class="btn btn-ghost btn-icon-xs rounded" title="Rename">
|
||||
<i class="text-xs text-gray-600 dark:text-gray-400 fas fa-pen"></i>
|
||||
</button>
|
||||
<button @click="moveLayer(layer.id, 'up')" class="btn btn-ghost btn-icon-xs rounded" title="Move up">
|
||||
<i class="text-xs text-gray-600 dark:text-gray-400 fas fa-chevron-up"></i>
|
||||
</button>
|
||||
<button @click="moveLayer(layer.id, 'down')" class="btn btn-ghost btn-icon-xs rounded" title="Move down">
|
||||
<i class="text-xs text-gray-600 dark:text-gray-400 fas fa-chevron-down"></i>
|
||||
</button>
|
||||
<button v-if="layers.length > 1" @click="removeLayer(layer.id)" class="btn btn-danger btn-icon-xs rounded" title="Delete">
|
||||
<i class="text-xs fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Grid Settings -->
|
||||
<section>
|
||||
<h3 class="flex items-center gap-2 mb-3 text-base font-bold text-gray-800 dark:text-gray-100">
|
||||
<i class="text-sm text-gray-700 dark:text-gray-300 fas fa-th"></i>
|
||||
Grid
|
||||
</h3>
|
||||
<div class="space-y-3">
|
||||
<div class="flex items-center justify-between px-3 py-2 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg">
|
||||
<label for="columns" class="text-sm font-medium text-gray-700 dark:text-gray-200">Columns</label>
|
||||
<input id="columns" type="number" v-model.number="columns" min="1" max="10" class="input-field w-16" />
|
||||
</div>
|
||||
|
||||
<div class="px-3 py-2 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg">
|
||||
<label class="flex items-center justify-between mb-2 cursor-pointer">
|
||||
<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-1.5 mt-2">
|
||||
<input type="number" v-model.number="settingsStore.manualCellWidth" min="1" max="2048" class="input-field w-full min-w-0" placeholder="W" />
|
||||
<span class="flex-shrink-0 text-gray-500 dark:text-gray-400">×</span>
|
||||
<input type="number" v-model.number="settingsStore.manualCellHeight" min="1" max="2048" class="input-field w-full min-w-0" placeholder="H" />
|
||||
<div class="flex items-center gap-0.5" v-if="editingLayerId !== layer.id">
|
||||
<button @click="startEditingLayer(layer.id, layer.name)" class="p-1 rounded hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors" title="Rename">
|
||||
<i class="fas fa-pen text-[9px] text-gray-400"></i>
|
||||
</button>
|
||||
<button @click="moveLayer(layer.id, 'up')" class="p-1 rounded hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors" title="Move up">
|
||||
<i class="fas fa-chevron-up text-[9px] text-gray-400"></i>
|
||||
</button>
|
||||
<button @click="moveLayer(layer.id, 'down')" class="p-1 rounded hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors" title="Move down">
|
||||
<i class="fas fa-chevron-down text-[9px] text-gray-400"></i>
|
||||
</button>
|
||||
<button v-if="layers.length > 1" @click="removeLayer(layer.id)" class="p-1 rounded hover:bg-red-50 dark:hover:bg-red-900/30 transition-colors" title="Delete">
|
||||
<i class="fas fa-trash text-[9px] text-red-400"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div v-else class="mt-1 text-xs font-mono text-gray-500 dark:text-gray-400 break-words">{{ cellSize.width }} × {{ cellSize.height }}px</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Alignment -->
|
||||
<!-- Grid & Cell Size Section (Combined) -->
|
||||
<section>
|
||||
<h3 class="flex items-center gap-2 mb-3 text-base font-bold text-gray-800 dark:text-gray-100">
|
||||
<i class="text-sm text-gray-700 dark:text-gray-300 fas fa-align-center"></i>
|
||||
Align
|
||||
</h3>
|
||||
<div class="grid grid-cols-3 gap-2">
|
||||
<button @click="alignSprites('left')" class="btn btn-secondary btn-sm" title="Left">
|
||||
<i class="fas fa-arrow-left"></i>
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<h3 class="flex items-center gap-1.5 text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wide">
|
||||
<i class="fas fa-th text-[10px]"></i>
|
||||
Grid Layout
|
||||
</h3>
|
||||
<div class="flex-1 h-px bg-gray-200 dark:bg-gray-700"></div>
|
||||
</div>
|
||||
<div class="bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 p-2.5 space-y-2">
|
||||
<!-- Columns -->
|
||||
<div class="flex items-center justify-between">
|
||||
<label for="columns" class="text-xs font-medium text-gray-600 dark:text-gray-300">Columns</label>
|
||||
<input id="columns" type="number" v-model.number="columns" min="1" max="10" class="w-14 px-2 py-1 text-xs text-center border border-gray-300 dark:border-gray-600 rounded bg-gray-50 dark:bg-gray-700 dark:text-gray-100 focus:ring-1 focus:ring-gray-500 outline-none" />
|
||||
</div>
|
||||
<!-- Cell Size -->
|
||||
<div class="flex items-center justify-between">
|
||||
<label class="text-xs font-medium text-gray-600 dark:text-gray-300 flex items-center gap-1.5">
|
||||
Cell Size
|
||||
<input type="checkbox" v-model="settingsStore.manualCellSizeEnabled" class="w-3 h-3 rounded" title="Manual override" />
|
||||
</label>
|
||||
<div v-if="settingsStore.manualCellSizeEnabled" class="flex items-center gap-1">
|
||||
<input type="number" v-model.number="settingsStore.manualCellWidth" min="1" max="2048" class="w-12 px-1.5 py-1 text-xs text-center border border-gray-300 dark:border-gray-600 rounded bg-gray-50 dark:bg-gray-700 dark:text-gray-100 outline-none" placeholder="W" />
|
||||
<span class="text-gray-400 text-xs">×</span>
|
||||
<input type="number" v-model.number="settingsStore.manualCellHeight" min="1" max="2048" class="w-12 px-1.5 py-1 text-xs text-center border border-gray-300 dark:border-gray-600 rounded bg-gray-50 dark:bg-gray-700 dark:text-gray-100 outline-none" placeholder="H" />
|
||||
</div>
|
||||
<span v-else class="text-xs font-mono text-gray-400">{{ cellSize.width }}×{{ cellSize.height }}px</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Sprite Alignment Section -->
|
||||
<section>
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<h3 class="flex items-center gap-1.5 text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wide">
|
||||
<i class="fas fa-align-center text-[10px]"></i>
|
||||
Align All Sprites
|
||||
</h3>
|
||||
<div class="flex-1 h-px bg-gray-200 dark:bg-gray-700"></div>
|
||||
</div>
|
||||
<div class="grid grid-cols-6 gap-1">
|
||||
<button @click="alignSprites('left')" class="p-2 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors" title="Align Left">
|
||||
<i class="fas fa-arrow-left text-xs text-gray-500"></i>
|
||||
</button>
|
||||
<button @click="alignSprites('center')" class="btn btn-secondary btn-sm" title="Center">
|
||||
<i class="fas fa-arrows-left-right"></i>
|
||||
<button @click="alignSprites('center')" class="p-2 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors" title="Center Horizontally">
|
||||
<i class="fas fa-arrows-left-right text-xs text-gray-500"></i>
|
||||
</button>
|
||||
<button @click="alignSprites('right')" class="btn btn-secondary btn-sm" title="Right">
|
||||
<i class="fas fa-arrow-right"></i>
|
||||
<button @click="alignSprites('right')" class="p-2 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors" title="Align Right">
|
||||
<i class="fas fa-arrow-right text-xs text-gray-500"></i>
|
||||
</button>
|
||||
<button @click="alignSprites('top')" class="btn btn-secondary btn-sm" title="Top">
|
||||
<i class="fas fa-arrow-up"></i>
|
||||
<button @click="alignSprites('top')" class="p-2 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors" title="Align Top">
|
||||
<i class="fas fa-arrow-up text-xs text-gray-500"></i>
|
||||
</button>
|
||||
<button @click="alignSprites('middle')" class="btn btn-secondary btn-sm" title="Middle">
|
||||
<i class="fas fa-arrows-up-down"></i>
|
||||
<button @click="alignSprites('middle')" class="p-2 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors" title="Center Vertically">
|
||||
<i class="fas fa-arrows-up-down text-xs text-gray-500"></i>
|
||||
</button>
|
||||
<button @click="alignSprites('bottom')" class="btn btn-secondary btn-sm" title="Bottom">
|
||||
<i class="fas fa-arrow-down"></i>
|
||||
<button @click="alignSprites('bottom')" class="p-2 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors" title="Align Bottom">
|
||||
<i class="fas fa-arrow-down text-xs text-gray-500"></i>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Export -->
|
||||
<!-- Export Section -->
|
||||
<section>
|
||||
<h3 class="flex items-center gap-2 mb-3 text-base font-bold text-gray-800 dark:text-gray-100">
|
||||
<i class="text-sm text-gray-700 dark:text-gray-300 fas fa-download"></i>
|
||||
Export
|
||||
</h3>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<button @click="downloadSpritesheet" class="btn btn-dark btn-sm" data-rybbit-event="download-spritesheet">
|
||||
<i class="fas fa-image"></i>
|
||||
<span>PNG</span>
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<h3 class="flex items-center gap-1.5 text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wide">
|
||||
<i class="fas fa-download text-[10px]"></i>
|
||||
Download & Share
|
||||
</h3>
|
||||
<div class="flex-1 h-px bg-gray-200 dark:bg-gray-700"></div>
|
||||
</div>
|
||||
<div class="grid grid-cols-4 gap-1.5 mb-2">
|
||||
<button @click="downloadSpritesheet" class="flex flex-col items-center justify-center p-2.5 bg-gray-700 hover:bg-gray-800 dark:bg-gray-600 dark:hover:bg-gray-500 rounded-lg text-white transition-colors" data-rybbit-event="download-spritesheet" title="Download as PNG image">
|
||||
<i class="fas fa-image text-sm mb-0.5"></i>
|
||||
<span class="text-[10px] font-medium">PNG</span>
|
||||
</button>
|
||||
<button @click="exportSpritesheetJSON" class="btn btn-dark btn-sm" data-rybbit-event="export-json">
|
||||
<i class="fas fa-file-code"></i>
|
||||
<span>JSON</span>
|
||||
<button @click="exportSpritesheetJSON" class="flex flex-col items-center justify-center p-2.5 bg-gray-700 hover:bg-gray-800 dark:bg-gray-600 dark:hover:bg-gray-500 rounded-lg text-white transition-colors" data-rybbit-event="export-json" title="Export project data as JSON">
|
||||
<i class="fas fa-file-code text-sm mb-0.5"></i>
|
||||
<span class="text-[10px] font-medium">JSON</span>
|
||||
</button>
|
||||
<button @click="openGifFpsModal" class="btn btn-dark btn-sm" data-rybbit-event="download-gif">
|
||||
<i class="fas fa-film"></i>
|
||||
<span>GIF</span>
|
||||
<button @click="openGifFpsModal" class="flex flex-col items-center justify-center p-2.5 bg-gray-700 hover:bg-gray-800 dark:bg-gray-600 dark:hover:bg-gray-500 rounded-lg text-white transition-colors" data-rybbit-event="download-gif" title="Export as animated GIF">
|
||||
<i class="fas fa-film text-sm mb-0.5"></i>
|
||||
<span class="text-[10px] font-medium">GIF</span>
|
||||
</button>
|
||||
<button @click="downloadAsZip" class="btn btn-dark btn-sm" data-rybbit-event="download-zip">
|
||||
<i class="fas fa-file-archive"></i>
|
||||
<span>ZIP</span>
|
||||
</button>
|
||||
<button @click="openShareModal" class="btn btn-dark btn-sm col-span-2" data-rybbit-event="share-spritesheet">
|
||||
<i class="fas fa-share-alt"></i>
|
||||
<span>Share</span>
|
||||
<button @click="downloadAsZip" class="flex flex-col items-center justify-center p-2.5 bg-gray-700 hover:bg-gray-800 dark:bg-gray-600 dark:hover:bg-gray-500 rounded-lg text-white transition-colors" data-rybbit-event="download-zip" title="Download all sprites as ZIP">
|
||||
<i class="fas fa-file-archive text-sm mb-0.5"></i>
|
||||
<span class="text-[10px] font-medium">ZIP</span>
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
@click="openShareModal"
|
||||
class="w-full flex items-center justify-center gap-2 p-2.5 bg-gradient-to-r from-blue-500 to-purple-600 hover:from-blue-600 hover:to-purple-700 rounded-lg text-white text-sm font-medium transition-all shadow-sm hover:shadow-md"
|
||||
data-rybbit-event="share-spritesheet"
|
||||
title="Generate shareable link"
|
||||
>
|
||||
<i class="fas fa-share-alt text-sm"></i>
|
||||
Share Online
|
||||
</button>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
@@ -224,41 +242,39 @@
|
||||
<div class="flex flex-col overflow-hidden min-h-0">
|
||||
<!-- Tab Navigation -->
|
||||
<div class="bg-gray-50/50 dark:bg-gray-900/30 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex items-center justify-between gap-1 p-2">
|
||||
<div class="flex items-center justify-between gap-1 px-3 py-2">
|
||||
<div class="flex gap-1">
|
||||
<button
|
||||
@click="activeTab = 'canvas'"
|
||||
class="border-gray-600 border"
|
||||
:class="[
|
||||
'flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-lg transition-all cursor-pointer',
|
||||
activeTab === 'canvas' ? 'bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 shadow-sm' : 'text-gray-600 dark:text-gray-400 hover:bg-white/50 dark:hover:bg-gray-800/50',
|
||||
'flex items-center gap-2 px-3 py-1.5 text-sm font-medium rounded-md transition-all cursor-pointer border',
|
||||
activeTab === 'canvas' ? 'bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 shadow-sm border-gray-300 dark:border-gray-600' : 'text-gray-500 dark:text-gray-400 hover:bg-white/50 dark:hover:bg-gray-800/50 border-transparent',
|
||||
]"
|
||||
>
|
||||
<i class="fas fa-th"></i>
|
||||
<i class="fas fa-th text-xs"></i>
|
||||
<span>Canvas</span>
|
||||
</button>
|
||||
<button
|
||||
@click="activeTab = 'preview'"
|
||||
class="border-gray-600 border"
|
||||
:class="[
|
||||
'flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-lg transition-all cursor-pointer',
|
||||
activeTab === 'preview' ? 'bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 shadow-sm' : 'text-gray-600 dark:text-gray-400 hover:bg-white/50 dark:hover:bg-gray-800/50',
|
||||
'flex items-center gap-2 px-3 py-1.5 text-sm font-medium rounded-md transition-all cursor-pointer border',
|
||||
activeTab === 'preview' ? 'bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 shadow-sm border-gray-300 dark:border-gray-600' : 'text-gray-500 dark:text-gray-400 hover:bg-white/50 dark:hover:bg-gray-800/50 border-transparent',
|
||||
]"
|
||||
data-rybbit-event="preview-animation"
|
||||
>
|
||||
<i class="fas fa-play"></i>
|
||||
<span>Preview</span>
|
||||
<i class="fas fa-play text-xs"></i>
|
||||
<span>Animation</span>
|
||||
</button>
|
||||
</div>
|
||||
<button @click="openShareModal" class="flex items-center gap-2 px-4 py-2 mr-2.5 text-sm font-medium rounded-lg bg-gradient-to-r from-blue-500 to-purple-600 text-white shadow-sm hover:shadow-md transition-all cursor-pointer" data-rybbit-event="share-spritesheet-header">
|
||||
<i class="fas fa-share-alt"></i>
|
||||
<span>Share spritesheet</span>
|
||||
<button @click="openShareModal" class="flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md bg-gradient-to-r from-blue-500 to-purple-600 text-white shadow-sm hover:shadow-md transition-all cursor-pointer" data-rybbit-event="share-spritesheet-header">
|
||||
<i class="fas fa-share-alt text-[10px]"></i>
|
||||
<span>Share</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tab Content -->
|
||||
<div class="p-6 lg:flex-1 lg:overflow-auto lg:min-h-0">
|
||||
<div class="p-4 lg:flex-1 lg:overflow-auto lg:min-h-0">
|
||||
<div v-if="activeTab === 'canvas'" class="h-full">
|
||||
<sprite-canvas
|
||||
:layers="layers"
|
||||
|
||||
Reference in New Issue
Block a user