[FEAT] Streamline UI
This commit is contained in:
72
src/components/utilities/CopyToFrameModal.vue
Normal file
72
src/components/utilities/CopyToFrameModal.vue
Normal file
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<Modal :is-open="isOpen" @close="close" title="Copy sprite to frame" :initialWidth="380" :initialHeight="320">
|
||||
<div class="p-4 flex flex-col h-full">
|
||||
<div class="space-y-4 flex-1">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Target frame</label>
|
||||
<select v-model.number="targetFrame" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-blue-500">
|
||||
<option v-for="i in maxFrameCount" :key="i" :value="i - 1">Frame {{ i }}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Target layer</label>
|
||||
<select v-model="targetLayerId" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-blue-500">
|
||||
<option v-for="layer in layers" :key="layer.id" :value="layer.id">{{ layer.name }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end gap-3 mt-6 pt-4 border-t border-gray-100 dark:border-gray-700">
|
||||
<button @click="close" class="px-4 py-2 text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors font-medium text-sm">Cancel</button>
|
||||
<button @click="confirm" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors font-medium text-sm">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from 'vue';
|
||||
import Modal from './Modal.vue';
|
||||
import type { Layer } from '@/types/sprites';
|
||||
|
||||
const props = defineProps<{
|
||||
isOpen: boolean;
|
||||
layers: Layer[];
|
||||
initialLayerId?: string;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'close'): void;
|
||||
(e: 'copy', targetLayerId: string, targetFrameIndex: number): void;
|
||||
}>();
|
||||
|
||||
const targetFrame = ref(0);
|
||||
const targetLayerId = ref('');
|
||||
|
||||
const maxFrameCount = computed(() => {
|
||||
const maxLen = Math.max(1, ...props.layers.map(l => l.sprites.length));
|
||||
return maxLen + 1; // Allow copying to one frame beyond current max
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.isOpen,
|
||||
isOpen => {
|
||||
if (isOpen) {
|
||||
targetFrame.value = 0;
|
||||
targetLayerId.value = props.initialLayerId || (props.layers.length > 0 ? props.layers[0].id : '');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const close = () => {
|
||||
emit('close');
|
||||
};
|
||||
|
||||
const confirm = () => {
|
||||
if (targetLayerId.value) {
|
||||
emit('copy', targetLayerId.value, targetFrame.value);
|
||||
close();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
Reference in New Issue
Block a user