Preserves the working F3 Phaser demo implementation before resetting the main frontend/ directory for a fresh start. The POC demonstrates: - Vue 3 + Phaser 3 integration - Real card rendering with images - Vue-Phaser state sync via gameBridge - Card interactions and damage counters To restore: copy .claude/frontend-poc/ back to frontend/ and run npm install
92 lines
2.3 KiB
Vue
92 lines
2.3 KiB
Vue
<script setup lang="ts">
|
|
/**
|
|
* Save/Cancel action buttons for deck builder.
|
|
*
|
|
* Displays the bottom action buttons with proper disabled states
|
|
* and loading indicator during save operations.
|
|
*
|
|
* @example
|
|
* ```vue
|
|
* <DeckActionButtons
|
|
* :can-save="canSave"
|
|
* :is-saving="isSaving"
|
|
* :is-dirty="isDirty"
|
|
* :deck-name="deckName"
|
|
* @save="handleSave"
|
|
* @cancel="handleCancel"
|
|
* />
|
|
* ```
|
|
*/
|
|
import { computed } from 'vue'
|
|
|
|
const props = defineProps<{
|
|
/** Whether the save operation is allowed (deck is valid, has name) */
|
|
canSave: boolean
|
|
/** Whether save is in progress */
|
|
isSaving: boolean
|
|
/** Whether there are unsaved changes */
|
|
isDirty: boolean
|
|
/** Current deck name (used for save button disabled state) */
|
|
deckName: string
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
save: []
|
|
cancel: []
|
|
}>()
|
|
|
|
/**
|
|
* Whether the save button should be disabled.
|
|
*/
|
|
const saveDisabled = computed(() => {
|
|
return props.isSaving || !props.isDirty || !props.deckName.trim()
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div class="flex gap-3 pt-4 border-t border-surface-light">
|
|
<button
|
|
type="button"
|
|
class="flex-1 px-4 py-2 rounded-lg font-medium bg-surface-light text-text hover:bg-surface-lighter active:scale-95 transition-all duration-150"
|
|
:disabled="isSaving"
|
|
@click="emit('cancel')"
|
|
>
|
|
Cancel
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="flex-1 px-4 py-2 rounded-lg font-medium bg-primary text-white hover:bg-primary-dark active:scale-95 transition-all duration-150 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
:disabled="saveDisabled"
|
|
@click="emit('save')"
|
|
>
|
|
<span
|
|
v-if="isSaving"
|
|
class="inline-flex items-center gap-2"
|
|
>
|
|
<svg
|
|
class="animate-spin h-4 w-4"
|
|
viewBox="0 0 24 24"
|
|
aria-hidden="true"
|
|
>
|
|
<circle
|
|
class="opacity-25"
|
|
cx="12"
|
|
cy="12"
|
|
r="10"
|
|
stroke="currentColor"
|
|
stroke-width="4"
|
|
fill="none"
|
|
/>
|
|
<path
|
|
class="opacity-75"
|
|
fill="currentColor"
|
|
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
|
|
/>
|
|
</svg>
|
|
<span>Saving...</span>
|
|
</span>
|
|
<span v-else>Save</span>
|
|
</button>
|
|
</div>
|
|
</template>
|