refactor: migrate remaining dialogs to Vue Transition, remove old keyframes
Convert Settings, Invoices, IdlePrompt, AppTrackingPrompt, and AppDiscard dialogs from animate-modal-enter CSS class to proper <Transition name="modal"> wrappers for enter/leave animations. Remove unused animate-modal-enter and animate-dropdown-enter keyframes.
This commit is contained in:
38
src/components/AppDiscardDialog.vue
Normal file
38
src/components/AppDiscardDialog.vue
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
defineProps<{ show: boolean }>()
|
||||||
|
defineEmits<{
|
||||||
|
cancel: []
|
||||||
|
discard: []
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Transition name="modal">
|
||||||
|
<div
|
||||||
|
v-if="show"
|
||||||
|
class="fixed inset-0 bg-black/70 backdrop-blur-[4px] flex items-center justify-center p-4 z-[60]"
|
||||||
|
@click.self="$emit('cancel')"
|
||||||
|
>
|
||||||
|
<div class="bg-bg-surface border border-border-subtle rounded-lg shadow-[0_1px_3px_rgba(0,0,0,0.3)] w-full max-w-xs p-6">
|
||||||
|
<h2 class="text-[1.125rem] font-semibold font-[family-name:var(--font-heading)] text-text-primary mb-2">Unsaved Changes</h2>
|
||||||
|
<p class="text-[0.75rem] text-text-secondary mb-6">
|
||||||
|
You have unsaved changes. Do you want to discard them?
|
||||||
|
</p>
|
||||||
|
<div class="flex justify-end gap-3">
|
||||||
|
<button
|
||||||
|
@click="$emit('cancel')"
|
||||||
|
class="px-4 py-2 border border-border-subtle text-text-secondary rounded-lg hover:bg-bg-elevated transition-colors duration-150"
|
||||||
|
>
|
||||||
|
Keep Editing
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click="$emit('discard')"
|
||||||
|
class="px-4 py-2 border border-status-error text-status-error font-medium rounded-lg hover:bg-status-error/10 transition-colors duration-150"
|
||||||
|
>
|
||||||
|
Discard
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
</template>
|
||||||
42
src/components/AppTrackingPromptDialog.vue
Normal file
42
src/components/AppTrackingPromptDialog.vue
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
interface Props {
|
||||||
|
show: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps<Props>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
continueTimer: []
|
||||||
|
stopTimer: []
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Transition name="modal">
|
||||||
|
<div
|
||||||
|
v-if="show"
|
||||||
|
class="fixed inset-0 bg-black/70 backdrop-blur-[4px] flex items-center justify-center p-4 z-50"
|
||||||
|
>
|
||||||
|
<div class="bg-bg-surface border border-border-subtle rounded-lg shadow-[0_1px_3px_rgba(0,0,0,0.3)] w-full max-w-sm p-6">
|
||||||
|
<h2 class="text-[1.125rem] font-semibold font-[family-name:var(--font-heading)] text-text-primary mb-2">Tracked app not visible</h2>
|
||||||
|
<p class="text-[0.75rem] text-text-secondary mb-6">
|
||||||
|
None of your tracked apps are currently visible on screen. The timer has been paused.
|
||||||
|
</p>
|
||||||
|
<div class="flex flex-col gap-2.5">
|
||||||
|
<button
|
||||||
|
@click="emit('continueTimer')"
|
||||||
|
class="w-full px-4 py-2.5 bg-accent text-bg-base text-[0.8125rem] font-medium rounded-lg hover:bg-accent-hover transition-colors duration-150"
|
||||||
|
>
|
||||||
|
Continue Timer
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click="emit('stopTimer')"
|
||||||
|
class="w-full px-4 py-2.5 border border-status-error text-status-error text-[0.8125rem] font-medium rounded-lg hover:bg-status-error/10 transition-colors duration-150"
|
||||||
|
>
|
||||||
|
Stop & Save
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
</template>
|
||||||
61
src/components/IdlePromptDialog.vue
Normal file
61
src/components/IdlePromptDialog.vue
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
show: boolean
|
||||||
|
idleSeconds: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
continueKeep: []
|
||||||
|
continueSubtract: []
|
||||||
|
stopTimer: []
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const idleFormatted = computed(() => {
|
||||||
|
const mins = Math.floor(props.idleSeconds / 60)
|
||||||
|
const secs = props.idleSeconds % 60
|
||||||
|
if (mins > 0) {
|
||||||
|
return `${mins}m ${secs}s`
|
||||||
|
}
|
||||||
|
return `${secs}s`
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Transition name="modal">
|
||||||
|
<div
|
||||||
|
v-if="show"
|
||||||
|
class="fixed inset-0 bg-black/70 backdrop-blur-[4px] flex items-center justify-center p-4 z-50"
|
||||||
|
>
|
||||||
|
<div class="bg-bg-surface border border-border-subtle rounded-lg shadow-[0_1px_3px_rgba(0,0,0,0.3)] w-full max-w-sm p-6">
|
||||||
|
<h2 class="text-[1.125rem] font-semibold font-[family-name:var(--font-heading)] text-text-primary mb-2">You've been idle</h2>
|
||||||
|
<p class="text-[0.75rem] text-text-secondary mb-6">
|
||||||
|
No keyboard or mouse input detected for <span class="font-mono font-medium text-text-primary">{{ idleFormatted }}</span>.
|
||||||
|
</p>
|
||||||
|
<div class="flex flex-col gap-2.5">
|
||||||
|
<button
|
||||||
|
@click="emit('continueKeep')"
|
||||||
|
class="w-full px-4 py-2.5 bg-accent text-bg-base text-[0.8125rem] font-medium rounded-lg hover:bg-accent-hover transition-colors duration-150"
|
||||||
|
>
|
||||||
|
Continue (keep time)
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click="emit('continueSubtract')"
|
||||||
|
class="w-full px-4 py-2.5 border border-border-visible text-text-primary text-[0.8125rem] rounded-lg hover:bg-bg-elevated transition-colors duration-150"
|
||||||
|
>
|
||||||
|
Continue (subtract {{ idleFormatted }})
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click="emit('stopTimer')"
|
||||||
|
class="w-full px-4 py-2.5 border border-status-error text-status-error text-[0.8125rem] font-medium rounded-lg hover:bg-status-error/10 transition-colors duration-150"
|
||||||
|
>
|
||||||
|
Stop & Save
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
</template>
|
||||||
@@ -160,38 +160,6 @@
|
|||||||
animation: pulse-seconds 1s ease-in-out infinite;
|
animation: pulse-seconds 1s ease-in-out infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Modal animations */
|
|
||||||
@keyframes modal-enter {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(-8px);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.animate-modal-enter {
|
|
||||||
animation: modal-enter 200ms ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dropdown animations */
|
|
||||||
@keyframes dropdown-enter {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(-4px) scale(0.98);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0) scale(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.animate-dropdown-enter {
|
|
||||||
animation: dropdown-enter 150ms ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Toast animations */
|
/* Toast animations */
|
||||||
@keyframes toast-enter {
|
@keyframes toast-enter {
|
||||||
from {
|
from {
|
||||||
|
|||||||
@@ -230,12 +230,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Invoice Detail Dialog -->
|
<!-- Invoice Detail Dialog -->
|
||||||
|
<Transition name="modal">
|
||||||
<div
|
<div
|
||||||
v-if="showDetailDialog"
|
v-if="showDetailDialog"
|
||||||
class="fixed inset-0 bg-black/70 backdrop-blur-[4px] flex items-center justify-center p-4 z-50"
|
class="fixed inset-0 bg-black/70 backdrop-blur-[4px] flex items-center justify-center p-4 z-50"
|
||||||
@click.self="showDetailDialog = false"
|
@click.self="showDetailDialog = false"
|
||||||
>
|
>
|
||||||
<div class="bg-bg-surface border border-border-subtle rounded-lg shadow-[0_1px_3px_rgba(0,0,0,0.3)] w-full max-w-2xl p-6 max-h-[calc(100vh-2rem)] overflow-y-auto animate-modal-enter">
|
<div class="bg-bg-surface border border-border-subtle rounded-lg shadow-[0_1px_3px_rgba(0,0,0,0.3)] w-full max-w-2xl p-6 max-h-[calc(100vh-2rem)] overflow-y-auto">
|
||||||
<div class="flex items-start justify-between mb-6">
|
<div class="flex items-start justify-between mb-6">
|
||||||
<div>
|
<div>
|
||||||
<h2 class="text-[1.75rem] font-bold font-[family-name:var(--font-heading)] tracking-tight text-text-primary">{{ selectedInvoice?.invoice_number }}</h2>
|
<h2 class="text-[1.75rem] font-bold font-[family-name:var(--font-heading)] tracking-tight text-text-primary">{{ selectedInvoice?.invoice_number }}</h2>
|
||||||
@@ -298,14 +299,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Transition>
|
||||||
|
|
||||||
<!-- Delete Confirmation Dialog -->
|
<!-- Delete Confirmation Dialog -->
|
||||||
|
<Transition name="modal">
|
||||||
<div
|
<div
|
||||||
v-if="showDeleteDialog"
|
v-if="showDeleteDialog"
|
||||||
class="fixed inset-0 bg-black/70 backdrop-blur-[4px] flex items-center justify-center p-4 z-50"
|
class="fixed inset-0 bg-black/70 backdrop-blur-[4px] flex items-center justify-center p-4 z-50"
|
||||||
@click.self="showDeleteDialog = false"
|
@click.self="showDeleteDialog = false"
|
||||||
>
|
>
|
||||||
<div class="bg-bg-surface border border-border-subtle rounded-lg shadow-[0_1px_3px_rgba(0,0,0,0.3)] w-full max-w-sm p-6 animate-modal-enter">
|
<div class="bg-bg-surface border border-border-subtle rounded-lg shadow-[0_1px_3px_rgba(0,0,0,0.3)] w-full max-w-sm p-6">
|
||||||
<h2 class="text-[1.125rem] font-semibold font-[family-name:var(--font-heading)] text-text-primary mb-2">Delete Invoice</h2>
|
<h2 class="text-[1.125rem] font-semibold font-[family-name:var(--font-heading)] text-text-primary mb-2">Delete Invoice</h2>
|
||||||
<p class="text-[0.75rem] text-text-secondary mb-6">
|
<p class="text-[0.75rem] text-text-secondary mb-6">
|
||||||
Are you sure you want to delete invoice "{{ invoiceToDelete?.invoice_number }}"? This action cannot be undone.
|
Are you sure you want to delete invoice "{{ invoiceToDelete?.invoice_number }}"? This action cannot be undone.
|
||||||
@@ -326,6 +329,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -471,12 +471,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Clear Data Confirmation Dialog -->
|
<!-- Clear Data Confirmation Dialog -->
|
||||||
|
<Transition name="modal">
|
||||||
<div
|
<div
|
||||||
v-if="showClearDataDialog"
|
v-if="showClearDataDialog"
|
||||||
class="fixed inset-0 bg-black/70 backdrop-blur-[4px] flex items-center justify-center p-4 z-50"
|
class="fixed inset-0 bg-black/70 backdrop-blur-[4px] flex items-center justify-center p-4 z-50"
|
||||||
@click.self="showClearDataDialog = false"
|
@click.self="showClearDataDialog = false"
|
||||||
>
|
>
|
||||||
<div class="bg-bg-surface border border-border-subtle rounded-lg shadow-[0_1px_3px_rgba(0,0,0,0.3)] w-full max-w-sm p-6 animate-modal-enter">
|
<div class="bg-bg-surface border border-border-subtle rounded-lg shadow-[0_1px_3px_rgba(0,0,0,0.3)] w-full max-w-sm p-6">
|
||||||
<h2 class="text-[1.125rem] font-semibold font-[family-name:var(--font-heading)] text-text-primary mb-2">Clear All Data</h2>
|
<h2 class="text-[1.125rem] font-semibold font-[family-name:var(--font-heading)] text-text-primary mb-2">Clear All Data</h2>
|
||||||
<p class="text-[0.75rem] text-text-secondary mb-4">
|
<p class="text-[0.75rem] text-text-secondary mb-4">
|
||||||
Are you sure? This action cannot be undone.
|
Are you sure? This action cannot be undone.
|
||||||
@@ -500,6 +501,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user