feat: add transitions and micro-interactions across all views

- Page transitions with slide-up/fade on route changes (App.vue)
- NavRail sliding active indicator with spring-like easing
- List enter/leave/move animations on Entries, Projects, Clients, Timer
- Modal enter/leave transitions with scale+fade on all dialogs
- Dropdown transitions with overshoot on all select/picker components
- Button feedback (scale on hover/active), card hover lift effects
- Timer pulse on start, glow on stop, floating empty state icons
- Content fade-in on Dashboard, Reports, Calendar, Timesheet
- Tag chip enter/leave animations in AppTagInput
- Progress bar smooth width transitions
- Implementation plan document
This commit is contained in:
Your Name
2026-02-18 11:33:58 +02:00
parent bd0dbaf91d
commit 04d4220604
16 changed files with 2115 additions and 144 deletions

View File

@@ -1,6 +1,7 @@
<script setup lang="ts">
import { ref, computed, onMounted, onBeforeUnmount, nextTick } from 'vue'
import { ChevronDown, Check } from 'lucide-vue-next'
import { computeDropdownPosition } from '../utils/dropdown'
interface Props {
modelValue: any
@@ -77,25 +78,9 @@ function isSelected(item: any): boolean {
return val === props.modelValue
}
function getZoomFactor(): number {
const app = document.getElementById('app')
if (!app) return 1
const zoom = (app.style as any).zoom
return zoom ? parseFloat(zoom) / 100 : 1
}
function updatePosition() {
if (!triggerRef.value) return
const rect = triggerRef.value.getBoundingClientRect()
const zoom = getZoomFactor()
panelStyle.value = {
position: 'fixed',
top: `${(rect.bottom + 4) / zoom}px`,
left: `${rect.left / zoom}px`,
width: `${rect.width / zoom}px`,
zIndex: '9999',
zoom: `${zoom * 100}%`,
}
panelStyle.value = computeDropdownPosition(triggerRef.value, { estimatedHeight: 280 })
}
function toggle() {
@@ -268,11 +253,12 @@ onBeforeUnmount(() => {
<!-- Dropdown panel -->
<Teleport to="body">
<Transition name="dropdown">
<div
v-if="isOpen"
ref="panelRef"
:style="panelStyle"
class="bg-bg-surface border border-border-visible rounded-xl shadow-[0_4px_24px_rgba(0,0,0,0.4)] overflow-hidden animate-dropdown-enter"
class="bg-bg-surface border border-border-visible rounded-xl shadow-[0_4px_24px_rgba(0,0,0,0.4)] overflow-hidden"
>
<div v-if="searchable" class="px-2 pt-2 pb-1">
<input
@@ -307,6 +293,7 @@ onBeforeUnmount(() => {
</div>
</div>
</div>
</Transition>
</Teleport>
</div>
</template>